Add optional screenshots to bug reports

This commit is contained in:
2026-09-15 21:16:06 +02:00
parent 39af9ecdb4
commit 5d3ae10036
10 changed files with 263 additions and 12 deletions
+19 -3
View File
@@ -7,10 +7,11 @@ from urllib.parse import quote, urlsplit
from uuid import UUID, uuid4
from zoneinfo import ZoneInfo
from fastapi import Form, Request
from fastapi import File, Form, Request, UploadFile
from fastapi.responses import HTMLResponse, RedirectResponse
from gitea_service import GiteaError, GiteaService, redact
from safe_images import MAX_SCREENSHOT_BYTES, ScreenshotError, sanitize_screenshot
from i18n import current_language, current_page, gettext as _
@@ -116,7 +117,7 @@ def register_routes(app, templates, get_db, get_user, service_factory=GiteaServi
description: str = Form(''), expected: str = Form(''), steps: str = Form(''),
category: str = Form('general'), severity: str = Form('normal'),
technical: bool = Form(False), route: str = Form('/'), platform: str = Form('Web'),
app_version: str = Form('')):
app_version: str = Form(''), screenshot: UploadFile | None = File(None)):
user = get_user(request)
if not user:
return RedirectResponse('/login', status_code=303)
@@ -132,6 +133,21 @@ def register_routes(app, templates, get_db, get_user, service_factory=GiteaServi
not 3 <= len(data['expected']) <= 3000 or len(data['steps']) > 3000 or
category not in CATEGORIES or severity not in SEVERITIES):
return render(user, data, identifier, _('Bitte prüfe die Pflichtfelder, Textlängen, Kategorie und den Schweregrad.'), 400)
screenshot_payload = None
if screenshot and screenshot.filename:
try:
raw_screenshot = screenshot.file.read(MAX_SCREENSHOT_BYTES + 1)
clean_bytes, extension, media_type = sanitize_screenshot(raw_screenshot)
screenshot_payload = (f'screenshot-{uuid4().hex}.{extension}', clean_bytes, media_type)
except ScreenshotError as error:
message = (_('Der Screenshot ist zu groß. Erlaubt sind maximal 4 MB.')
if error.code == 'too_large' else
_('Bitte lade ein gültiges JPG-, PNG- oder WebP-Bild hoch.'))
return render(user, data, identifier, message, 400)
finally:
screenshot.file.close()
with get_db() as connection:
# Cross-worker cooldown and one-use form IDs, serialized for each reporter.
connection.execute('SELECT pg_advisory_xact_lock(71002, %s)', (user['id'],))
@@ -152,7 +168,7 @@ def register_routes(app, templates, get_db, get_user, service_factory=GiteaServi
connection.execute('UPDATE bug_report_submissions SET state=\'sending\',submitted_at=CURRENT_TIMESTAMP WHERE id=%s', (identifier,))
connection.commit()
try:
number = service_factory().create_issue(data['title'], issue_body(data, user, request), category)
number = service_factory().create_issue(data['title'], issue_body(data, user, request), category, screenshot=screenshot_payload)
except GiteaError as error:
with get_db() as connection:
connection.execute('UPDATE bug_report_submissions SET state=%s WHERE id=%s',