Erreur de sécurité
+Votre session a expiré. Veuillez rafraîchir la page.
+ + '''), 400 + +@app.errorhandler(429) +def ratelimit_handler(e): + """Gestion du rate limiting""" + return render_template_string(''' + + +Trop de requêtes
+Vous avez dépassé la limite. Réessayez dans quelques minutes.
+ + '''), 429 + +# --- ROUTES --- + +@app.route('/', methods=['GET', 'POST']) +@limiter.limit("10 per minute", methods=["POST"]) +def index(): + """Page principale avec formulaire de post""" + conn = get_db_connection() + cursor = conn.cursor(dictionary=True) + error = None + success = None + + if request.method == 'POST': + client_ip = get_real_ip() + + # Vérification Anti-Spam + cursor.execute( + "SELECT created_at FROM posts WHERE ip_address = %s ORDER BY created_at DESC LIMIT 1", + (client_ip,) + ) + last_post = cursor.fetchone() + + can_post = True + if last_post: + time_since_last = datetime.datetime.now() - last_post['created_at'] + if time_since_last.total_seconds() < 60: + error = "Hé ho ! Une minute entre chaque post svp." + can_post = False + + if can_post: + content = request.form.get('content', '').strip() + file = request.files.get('image') + + # Validation du contenu + if content and len(content) > 5000: + error = "Message trop long (max 5000 caractères)" + can_post = False + + filename = None + if can_post and file and file.filename: + original_filename = sanitize_filename(file.filename) + + if allowed_file(original_filename): + # VALIDATION STRICTE DU CONTENU + is_valid, validation_result = validate_image_file(file.stream) + + if is_valid: + ext = original_filename.rsplit('.', 1)[1].lower() + # Nom de fichier sécurisé avec UUID + filename = f"{uuid.uuid4()}.{ext}" + filepath = UPLOAD_FOLDER / filename + + try: + file.save(str(filepath)) + # Vérifie que le fichier a bien été écrit + if not filepath.exists(): + error = "Erreur lors de la sauvegarde du fichier" + can_post = False + filename = None + except Exception as e: + app.logger.error(f"Erreur sauvegarde fichier: {e}") + error = "Erreur lors de l'upload" + can_post = False + filename = None + else: + error = f"Fichier non valide: {validation_result}" + can_post = False + else: + error = "Extension de fichier non autorisée" + can_post = False + + if can_post and (content or filename): + try: + cursor.execute( + "INSERT INTO posts (content, image_filename, ip_address) VALUES (%s, %s, %s)", + (content, filename, client_ip) + ) + conn.commit() + success = "Message posté avec succès !" + except Exception as e: + app.logger.error(f"Erreur insertion DB: {e}") + error = "Erreur lors de la publication" + # Nettoie le fichier uploadé en cas d'erreur DB + if filename: + try: + (UPLOAD_FOLDER / filename).unlink(missing_ok=True) + except: + pass + elif can_post: + error = "Veuillez saisir un message ou une image" + + # Récupération des posts avec limite + cursor.execute("SELECT * FROM posts ORDER BY created_at DESC LIMIT 50") + posts = cursor.fetchall() + cursor.close() + conn.close() + + # Template avec auto-escape activé par défaut + html = """ + + + + + +📝 Le Forum de la Team M
+ + {% if error %} ++ + {% if posts %} + {% for post in posts %} +
+ Aucun message pour le moment. Soyez le premier à poster ! 🎉 +
+