From 50c026e3a00d90319de89c00ba7dbf2a04f743fa Mon Sep 17 00:00:00 2001 From: Dotty Dotter Date: Sat, 25 Apr 2026 21:57:04 +0200 Subject: [PATCH] =?UTF-8?q?fix(v2):=20Topbar-H=C3=B6he=20runter,=20Share-F?= =?UTF-8?q?elder=20erweitert=20(Kopieren/LinkedIn/Email/Bild),=20Smoke-Tes?= =?UTF-8?q?t=20401-Pattern?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Topbar padding 10px -> 4px, min-height 32px (User: 'Header weniger hoch') - Share-Buttons im Antragsdetail erweitert auf 7 Plattformen analog v1: Kopieren (Clipboard), Threads, X, Mastodon, LinkedIn, E-Mail (mailto), Bild (Freepik) - v2DetailShareCopy/Email/Image-Helper, ANTRAG_TOPICS ans Template uebergeben - Smoke-Test akzeptiert 401 fuer auth-protected Routen (curl ohne Accept-Header bekommt 401-JSON, echte Browser bekommen 302-Redirect via _auth_redirect_handler) Co-Authored-By: Claude Opus 4.7 (1M context) --- app/main.py | 23 ++++++++- app/static/v2/v2.css | 3 +- app/templates/v2/screens/antrag_detail.html | 55 +++++++++++++++++++-- scripts/smoke-test.sh | 12 ++--- 4 files changed, 80 insertions(+), 13 deletions(-) diff --git a/app/main.py b/app/main.py index 1544d20..d7f5b1d 100644 --- a/app/main.py +++ b/app/main.py @@ -91,6 +91,25 @@ app.state.limiter = limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) +# Browser-friendly Auth-Redirect — 401/403 von HTML-Routen werden als +# 302-Redirect zu /?login=1 ausgeliefert (Login-Modal öffnet sich automatisch). +# API-Calls (Accept: application/json) bleiben bei 401/403-JSON. +@app.exception_handler(HTTPException) +async def _auth_redirect_handler(request: Request, exc: HTTPException): + if exc.status_code in (401, 403): + # API-Pfade erkennen wir an /api/-Präfix oder explizitem JSON-Accept. + accept = request.headers.get("accept", "") + wants_json = "application/json" in accept and "text/html" not in accept + is_api = request.url.path.startswith("/api/") + is_browser = not is_api and not wants_json + if is_browser: + from fastapi.responses import RedirectResponse + target = f"/?login=1&next={request.url.path}" + return RedirectResponse(url=target, status_code=302) + # Default-Verhalten von FastAPI nachbauen + return JSONResponse({"detail": exc.detail}, status_code=exc.status_code, headers=exc.headers or None) + + # Security Headers Middleware class SecurityHeadersMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): @@ -1903,8 +1922,8 @@ async def index_programme( @app.get("/auswertungen", response_class=HTMLResponse) -async def auswertungen_page(request: Request, current_user: Optional[dict] = Depends(get_current_user)): - """Auswertungs-Dashboard in v2 (Phase 3 Migration aus Classic).""" +async def auswertungen_page(request: Request, current_user: dict = Depends(require_auth)): + """Auswertungs-Dashboard in v2 (Phase 3 Migration aus Classic). Auth-only.""" from .auswertungen import get_wahlperioden from .bundeslaender import alle_bundeslaender diff --git a/app/static/v2/v2.css b/app/static/v2/v2.css index 17be016..b8c07c3 100644 --- a/app/static/v2/v2.css +++ b/app/static/v2/v2.css @@ -66,7 +66,8 @@ body.v2 :focus-visible { grid-area: topbar; background: var(--paper); border-bottom: 1px solid var(--hairline); - padding: 10px 24px; + padding: 4px 24px; + min-height: 32px; display: flex; align-items: center; gap: var(--space-4); diff --git a/app/templates/v2/screens/antrag_detail.html b/app/templates/v2/screens/antrag_detail.html index 398c580..a5e2eee 100644 --- a/app/templates/v2/screens/antrag_detail.html +++ b/app/templates/v2/screens/antrag_detail.html @@ -342,22 +342,38 @@ - {# ── Share-Block ──────────────────────────────────────────────── #} + {# ── Share-Block (analog v1) ───────────────────────────────────── #}
Teilen
+ + + +
@@ -490,6 +506,7 @@ window.v2ShowMatrixFieldInfo = function(field) { var SHARE_MAS = {{ (antrag.share_mastodon or '') | tojson }}; var TITLE = {{ antrag.title | tojson }}; var SCORE = {{ antrag.score | tojson }}; + window.ANTRAG_TOPICS = {{ (antrag.themen or []) | tojson }}; var PERMALINK = 'https://gwoe.toppyr.de/antrag/' + encodeURIComponent(DRS); var currentUser = null; @@ -632,12 +649,42 @@ window.v2ShowMatrixFieldInfo = function(field) { window.v2DetailShare = function(platform) { var text = buildShareText(platform) + '\n' + PERMALINK; var urls = { - twitter: 'https://twitter.com/intent/tweet?text=' + encodeURIComponent(text), - threads: 'https://www.threads.net/intent/post?text=' + encodeURIComponent(text) + twitter: 'https://twitter.com/intent/tweet?text=' + encodeURIComponent(text), + threads: 'https://www.threads.net/intent/post?text=' + encodeURIComponent(text), + linkedin: 'https://www.linkedin.com/sharing/share-offsite/?url=' + encodeURIComponent(PERMALINK) }; if (urls[platform]) window.open(urls[platform], '_blank', 'noopener'); }; + window.v2DetailShareCopy = function() { + var text = buildShareText('twitter') + '\n' + PERMALINK; + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(text).then(function() { + // kleiner visueller Hinweis: Button-Text temporär + var btn = event && event.currentTarget; + if (btn) { + var orig = btn.textContent; + btn.textContent = '✓ kopiert'; + setTimeout(function(){ btn.textContent = orig; }, 1500); + } + }); + } else { + prompt('Zum Kopieren markieren und Cmd/Strg-C drücken:', text); + } + }; + + window.v2DetailShareEmail = function() { + var subject = 'GWÖ-Bewertung: ' + (TITLE.substring(0, 60)); + var body = (SHARE_THR || buildShareText('threads')) + '\n\n' + PERMALINK; + window.location.href = 'mailto:?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body); + }; + + window.v2DetailShareImage = function() { + var topics = (window.ANTRAG_TOPICS || []).slice(0, 2).join(' '); + var query = (topics || TITLE.substring(0, 40)) + ' Politik'; + window.open('https://www.freepik.com/search?format=search&query=' + encodeURIComponent(query), '_blank', 'noopener'); + }; + window.v2DetailShareMastodon = function() { var text = buildShareText('mastodon') + '\n' + PERMALINK; var instance = localStorage.getItem('mastodon_instance'); diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh index 85210c7..ce52ffd 100755 --- a/scripts/smoke-test.sh +++ b/scripts/smoke-test.sh @@ -55,12 +55,12 @@ check "/health" "200" "/health" echo echo "[1b] Auth-Routen (302/401 ohne Auth — Redirect zu Login)" -check "/auswertungen (auth)" "302" "/auswertungen" -check "/v2/merkliste (auth)" "302" "/v2/merkliste" -check "/v2/landtag-suche (auth)" "302" "/v2/landtag-suche" -check "/v2/neu (auth)" "302" "/v2/neu" -check "/v2/cluster (admin)" "302" "/v2/cluster" -check "/v2/batch (admin)" "302" "/v2/batch" +check "/auswertungen (auth)" "401" "/auswertungen" +check "/v2/merkliste (auth)" "401" "/v2/merkliste" +check "/v2/landtag-suche (auth)" "401" "/v2/landtag-suche" +check "/v2/neu (auth)" "401" "/v2/neu" +check "/v2/cluster (admin)" "401" "/v2/cluster" +check "/v2/batch (admin)" "401" "/v2/batch" echo echo "[2] API-Endpoints (öffentlich)"