Spaces:
Build error
Build error
Update web_routes.py
Browse files- web_routes.py +127 -51
web_routes.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
# web_routes.py
|
| 2 |
-
|
| 3 |
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify, send_from_directory, current_app # <-- NOUVEAUX IMPORTS
|
| 4 |
from werkzeug.utils import secure_filename # <-- NOUVEL IMPORT (pourrait ne pas être nécessaire avec uuid, mais sécurisant)
|
| 5 |
import os
|
|
@@ -7,11 +7,13 @@ import uuid # <-- NOUVEL IMPORT
|
|
| 7 |
from auth_backend import get_plan_details
|
| 8 |
from baserow_storage import get_health_status
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
| 13 |
return '.' in filename and \
|
| 14 |
-
filename.rsplit('.', 1)[1].lower() in
|
| 15 |
|
| 16 |
# Création du Blueprint 'web_bp'
|
| 17 |
web_bp = Blueprint('web_bp', __name__)
|
|
@@ -101,59 +103,133 @@ def html_launcher():
|
|
| 101 |
# Le template 'html_launcher.html' doit être créé.
|
| 102 |
return render_template("html_launcher.html")
|
| 103 |
|
| 104 |
-
|
| 105 |
-
|
|
|
|
|
|
|
|
|
|
| 106 |
"""
|
| 107 |
-
|
| 108 |
"""
|
| 109 |
-
#
|
| 110 |
-
|
| 111 |
-
return jsonify({"status": "error", "message": "Aucun fichier n'a été envoyé."}), 400
|
| 112 |
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
|
| 115 |
-
#
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
}), 200
|
| 137 |
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
"""
|
| 147 |
-
Sert
|
|
|
|
| 148 |
"""
|
| 149 |
-
# Sécurité: Rejeter toute tentative de traversée de répertoire (..)
|
| 150 |
-
if '..' in filename
|
| 151 |
-
return "Accès
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
|
| 153 |
-
# Utiliser send_from_directory pour servir le fichier
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# web_routes.py
|
| 2 |
+
import shutil
|
| 3 |
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify, send_from_directory, current_app # <-- NOUVEAUX IMPORTS
|
| 4 |
from werkzeug.utils import secure_filename # <-- NOUVEL IMPORT (pourrait ne pas être nécessaire avec uuid, mais sécurisant)
|
| 5 |
import os
|
|
|
|
| 7 |
from auth_backend import get_plan_details
|
| 8 |
from baserow_storage import get_health_status
|
| 9 |
|
| 10 |
+
# NOUVEAU : Fonction pour vérifier les extensions autorisées pour le déploiement statique
|
| 11 |
+
def allowed_static_file(filename):
|
| 12 |
+
"""Vérifie si le fichier a une extension autorisée (HTML, CSS, JS)."""
|
| 13 |
+
# Ajout de .css et .js aux extensions autorisées
|
| 14 |
+
STATIC_ALLOWED_EXTENSIONS = {'html', 'htm', 'css', 'js', 'jpg', 'jpeg', 'png', 'gif', 'svg'}
|
| 15 |
return '.' in filename and \
|
| 16 |
+
filename.rsplit('.', 1)[1].lower() in STATIC_ALLOWED_EXTENSIONS
|
| 17 |
|
| 18 |
# Création du Blueprint 'web_bp'
|
| 19 |
web_bp = Blueprint('web_bp', __name__)
|
|
|
|
| 103 |
# Le template 'html_launcher.html' doit être créé.
|
| 104 |
return render_template("html_launcher.html")
|
| 105 |
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
# --- NOUVELLE ROUTE : Page de Déploiement Statique ---
|
| 109 |
+
@web_bp.route("/static-deploy")
|
| 110 |
+
def static_deploy_page():
|
| 111 |
"""
|
| 112 |
+
Route pour afficher l'interface de déploiement statique.
|
| 113 |
"""
|
| 114 |
+
# Récupérer l'ID de déploiement temporaire de la session s'il existe
|
| 115 |
+
deploy_id = session.get('current_deploy_id')
|
|
|
|
| 116 |
|
| 117 |
+
# Si un ID existe, on peut récupérer la liste des fichiers pour l'afficher
|
| 118 |
+
files = []
|
| 119 |
+
base_dir = None
|
| 120 |
+
if deploy_id:
|
| 121 |
+
base_dir = os.path.join(current_app.config['UPLOAD_FOLDER'], deploy_id)
|
| 122 |
+
if os.path.exists(base_dir):
|
| 123 |
+
# Parcourir le dossier pour lister les fichiers
|
| 124 |
+
for root, _, filenames in os.walk(base_dir):
|
| 125 |
+
for filename in filenames:
|
| 126 |
+
# Rendre le chemin relatif à la base_dir
|
| 127 |
+
relative_path = os.path.relpath(os.path.join(root, filename), base_dir)
|
| 128 |
+
files.append(relative_path)
|
| 129 |
|
| 130 |
+
# L'état du bouton "Lancer"
|
| 131 |
+
index_file_present = 'index.html' in files
|
| 132 |
+
|
| 133 |
+
return render_template("static_deploy.html",
|
| 134 |
+
files=files,
|
| 135 |
+
index_file_present=index_file_present,
|
| 136 |
+
deploy_id=deploy_id)
|
| 137 |
+
|
| 138 |
+
# --- NOUVELLE ROUTE : Upload de Fichiers Statiques Multiples ---
|
| 139 |
+
@web_bp.route("/upload-static", methods=['POST'])
|
| 140 |
+
def upload_static_files():
|
| 141 |
+
"""
|
| 142 |
+
Gère le téléversement de multiples fichiers statiques (HTML, CSS, JS, images).
|
| 143 |
+
Crée un dossier temporaire unique par session/déploiement.
|
| 144 |
+
"""
|
| 145 |
+
# 1. Obtenir un ID de déploiement unique pour cette session (ou en créer un)
|
| 146 |
+
deploy_id = session.get('current_deploy_id')
|
| 147 |
+
if not deploy_id:
|
| 148 |
+
deploy_id = str(uuid.uuid4())
|
| 149 |
+
session['current_deploy_id'] = deploy_id
|
| 150 |
+
|
| 151 |
+
# 2. Définir le chemin de base du dossier d'upload
|
| 152 |
+
base_deploy_path = os.path.join(current_app.config['UPLOAD_FOLDER'], deploy_id)
|
| 153 |
+
|
| 154 |
+
# 3. Supprimer l'ancien dossier s'il existe (pour un nouveau déploiement)
|
| 155 |
+
# L'utilisateur doit effacer le contenu existant ou le remplacer
|
| 156 |
+
if os.path.exists(base_deploy_path):
|
| 157 |
+
# Pour simplifier, on supprime tout avant le nouvel upload
|
| 158 |
+
shutil.rmtree(base_deploy_path)
|
| 159 |
|
| 160 |
+
os.makedirs(base_deploy_path) # Créer le nouveau dossier
|
| 161 |
+
|
| 162 |
+
# 4. Vérification de la présence de fichiers
|
| 163 |
+
if not request.files:
|
| 164 |
+
return jsonify({"status": "error", "message": "Aucun fichier n'a été envoyé."}), 400
|
| 165 |
+
|
| 166 |
+
uploaded_files_list = []
|
| 167 |
+
index_html_present = False
|
| 168 |
+
|
| 169 |
+
# 5. Parcourir tous les fichiers envoyés
|
| 170 |
+
for key, file in request.files.items():
|
| 171 |
+
if file.filename == '':
|
| 172 |
+
continue
|
| 173 |
|
| 174 |
+
if file and allowed_static_file(file.filename):
|
| 175 |
+
# Sécurité: Utiliser secure_filename et s'assurer que le chemin est plat
|
| 176 |
+
filename = secure_filename(file.filename)
|
| 177 |
+
save_path = os.path.join(base_deploy_path, filename)
|
|
|
|
| 178 |
|
| 179 |
+
try:
|
| 180 |
+
file.save(save_path)
|
| 181 |
+
uploaded_files_list.append(filename)
|
| 182 |
+
if filename.lower() == 'index.html':
|
| 183 |
+
index_html_present = True
|
| 184 |
+
|
| 185 |
+
except Exception as e:
|
| 186 |
+
current_app.logger.error(f"Erreur lors de la sauvegarde du fichier {filename}: {e}")
|
| 187 |
+
# En cas d'erreur critique, on pourrait vouloir rollback (supprimer le dossier)
|
| 188 |
+
return jsonify({"status": "error", "message": f"Erreur serveur lors du téléversement de {filename}."}), 500
|
| 189 |
+
else:
|
| 190 |
+
return jsonify({"status": "error", "message": f"Type de fichier non autorisé: {file.filename}."}), 400
|
| 191 |
+
|
| 192 |
+
if not uploaded_files_list:
|
| 193 |
+
return jsonify({"status": "error", "message": "Aucun fichier valide n'a été téléversé."}), 400
|
| 194 |
+
|
| 195 |
+
# 6. Générer l'URL de base pour le lancement (doit pointer vers serve_static)
|
| 196 |
+
launch_url = url_for('web_bp.serve_static', deploy_id=deploy_id, filename='index.html')
|
| 197 |
+
|
| 198 |
+
return jsonify({
|
| 199 |
+
"status": "success",
|
| 200 |
+
"message": "Fichiers téléversés avec succès.",
|
| 201 |
+
"file_count": len(uploaded_files_list),
|
| 202 |
+
"index_present": index_html_present,
|
| 203 |
+
"launch_url": launch_url,
|
| 204 |
+
"deploy_id": deploy_id
|
| 205 |
+
}), 200
|
| 206 |
+
|
| 207 |
+
# --- NOUVELLE ROUTE : Servir les Fichiers Statiques ---
|
| 208 |
+
@web_bp.route("/serve-static/<deploy_id>/<path:filename>")
|
| 209 |
+
def serve_static(deploy_id, filename):
|
| 210 |
"""
|
| 211 |
+
Sert les fichiers statiques (HTML, CSS, JS, images) à partir du dossier
|
| 212 |
+
temporaire identifié par deploy_id.
|
| 213 |
"""
|
| 214 |
+
# 1. Sécurité: Rejeter toute tentative de traversée de répertoire (..)
|
| 215 |
+
if '..' in filename:
|
| 216 |
+
return "Accès refusé: Tentative de traversée de répertoire", 403
|
| 217 |
+
|
| 218 |
+
# 2. Définir le répertoire de base de l'ID de déploiement
|
| 219 |
+
root_dir = os.path.join(current_app.config['UPLOAD_FOLDER'], deploy_id)
|
| 220 |
+
|
| 221 |
+
# 3. Vérifier que le fichier est bien autorisé (pour la sécurité, même si on utilise send_from_directory)
|
| 222 |
+
if not allowed_static_file(filename):
|
| 223 |
+
return "Type de fichier non autorisé", 403
|
| 224 |
|
| 225 |
+
# 4. Utiliser send_from_directory pour servir le fichier
|
| 226 |
+
try:
|
| 227 |
+
# send_from_directory va gérer les mimetypes
|
| 228 |
+
return send_from_directory(
|
| 229 |
+
root_dir,
|
| 230 |
+
filename,
|
| 231 |
+
conditional=True # Support des requêtes conditionnelles (mise en cache)
|
| 232 |
+
)
|
| 233 |
+
except Exception:
|
| 234 |
+
# Fichier non trouvé ou autre erreur
|
| 235 |
+
return "Fichier non trouvé", 404
|