Add image attachments to feedback form (JPEG/PNG)
- File input in feedback modal (max 3 images, 5 MB each) - Frontend validation for file count and size - Backend: multipart/form-data with UploadFile, MIME attachments - Images attached to feedback email as base64-encoded attachments - Only JPEG and PNG allowed (validated server-side) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -5,12 +5,14 @@ import logging
|
||||
from collections import defaultdict
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.base import MIMEBase
|
||||
from email import encoders
|
||||
from typing import List
|
||||
|
||||
import aiosmtplib
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Form, UploadFile, File
|
||||
|
||||
from auth import get_current_user
|
||||
from models import FeedbackRequest
|
||||
from config import (
|
||||
SMTP_HOST,
|
||||
SMTP_PORT,
|
||||
@@ -38,12 +40,23 @@ _MAX_PER_HOUR = 3
|
||||
_WINDOW = 3600
|
||||
|
||||
|
||||
_ALLOWED_TYPES = {"image/jpeg", "image/png"}
|
||||
_MAX_FILE_SIZE = 5 * 1024 * 1024 # 5 MB
|
||||
_MAX_FILES = 3
|
||||
|
||||
|
||||
@router.post("/feedback", status_code=204)
|
||||
async def send_feedback(
|
||||
data: FeedbackRequest,
|
||||
category: str = Form(...),
|
||||
message: str = Form(..., min_length=10, max_length=5000),
|
||||
files: List[UploadFile] = File(default=[]),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Feedback per E-Mail an das Team senden."""
|
||||
"""Feedback per E-Mail an das Team senden (mit optionalen Bild-Anhaengen)."""
|
||||
# Kategorie validieren
|
||||
if category not in CATEGORY_LABELS:
|
||||
raise HTTPException(status_code=422, detail="Ungueltige Kategorie.")
|
||||
|
||||
user_id = current_user["id"]
|
||||
|
||||
# Rate-Limiting
|
||||
@@ -56,6 +69,13 @@ async def send_feedback(
|
||||
detail="Maximal 3 Feedback-Nachrichten pro Stunde. Bitte spaeter erneut versuchen.",
|
||||
)
|
||||
|
||||
# Dateien validieren
|
||||
if len(files) > _MAX_FILES:
|
||||
raise HTTPException(status_code=422, detail=f"Maximal {_MAX_FILES} Dateien erlaubt.")
|
||||
for f in files:
|
||||
if f.content_type not in _ALLOWED_TYPES:
|
||||
raise HTTPException(status_code=422, detail=f"Dateityp {f.content_type} nicht erlaubt (nur JPEG/PNG).")
|
||||
|
||||
if not SMTP_HOST:
|
||||
logger.warning("SMTP nicht konfiguriert - Feedback nicht gesendet")
|
||||
raise HTTPException(
|
||||
@@ -65,8 +85,8 @@ async def send_feedback(
|
||||
|
||||
email = current_user.get("email", "")
|
||||
display_name = email.split("@")[0] if email else "Unbekannt"
|
||||
category_label = CATEGORY_LABELS.get(data.category, data.category)
|
||||
message_escaped = html.escape(data.message)
|
||||
category_label = CATEGORY_LABELS.get(category, category)
|
||||
message_escaped = html.escape(message)
|
||||
|
||||
subject = f"[AegisSight Feedback] {category_label} von {display_name}"
|
||||
html_body = f"""\
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren