From b0ec842804e65976ff298d7d0d63319e582c5f28 Mon Sep 17 00:00:00 2001 From: ZdenekSrotyr Date: Sat, 2 May 2026 20:55:03 +0200 Subject: [PATCH] =?UTF-8?q?feat(admin-ui):=20SRI=20+=20CDN=20fallback=20fo?= =?UTF-8?q?r=20CodeMirror,=20301=E2=86=92302=20on=20/install,=20error=20sa?= =?UTF-8?q?nitization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add integrity= + crossorigin= to all 4 cdnjs tags in admin_welcome.html and admin_setup_banner.html (I-1) - Add graceful CDN fallback: when CodeMirror is undefined (SRI mismatch or CDN down), degrade to styled plain textarea with polyfill editor interface so save/reset/preview still work (I-1) - Replace fixed 480px editor height with calc(100vh - 320px) for viewport-relative sizing; add min-height: 480px to .welcome-editor-col (M-8) - Change /install redirect from 301 to 302 to prevent indefinite browser caching (I-5) - Sanitize Jinja2 error detail in /api/welcome 500 response: log full error server-side, return generic detail pointing at /admin/welcome (M-7) - Hoist build_context import to module level in app/api/welcome.py (M-11) --- app/api/welcome.py | 10 ++-- app/web/router.py | 9 +++- app/web/templates/admin_setup_banner.html | 62 ++++++++++++++++++----- app/web/templates/admin_welcome.html | 62 ++++++++++++++++++----- 4 files changed, 109 insertions(+), 34 deletions(-) diff --git a/app/api/welcome.py b/app/api/welcome.py index dfd707d..159074f 100644 --- a/app/api/welcome.py +++ b/app/api/welcome.py @@ -7,6 +7,7 @@ """ import datetime +import logging from typing import Optional import duckdb @@ -17,7 +18,9 @@ from pydantic import BaseModel, Field from app.auth.access import require_admin from app.auth.dependencies import _get_db, get_current_user from src.repositories.welcome_template import WelcomeTemplateRepository -from src.welcome_template import _load_default_template, render_welcome +from src.welcome_template import _load_default_template, build_context, render_welcome + +logger = logging.getLogger(__name__) router = APIRouter(tags=["welcome"]) @@ -75,9 +78,10 @@ async def get_welcome( try: rendered = render_welcome(conn, user=user, server_url=server_url) except TemplateError as e: + logger.warning("Welcome render failed: %s", e, exc_info=True) raise HTTPException( status_code=500, - detail=f"Welcome template render failed: {e}. An admin can reset it via /admin/welcome.", + detail="Welcome template render failed. An admin can fix it at /admin/welcome.", ) return WelcomeResponse(content=rendered) @@ -133,8 +137,6 @@ async def admin_preview_template( """Render arbitrary template content against the live context for the calling admin, without persisting. Used by the /admin/welcome editor's Preview button so admins can see their edits before saving.""" - from src.welcome_template import build_context - env = Environment(undefined=StrictUndefined, autoescape=False) try: template = env.from_string(payload.content) diff --git a/app/web/router.py b/app/web/router.py index 87abbd5..962f746 100644 --- a/app/web/router.py +++ b/app/web/router.py @@ -744,8 +744,13 @@ async def setup_page( @router.get("/install", response_class=HTMLResponse) async def install_redirect(request: Request): - """Backwards-compat redirect: /install → /setup (301).""" - return RedirectResponse(url="/setup", status_code=301) + """Backwards-compat redirect: /install → /setup (302). + + Using 302 (temporary) rather than 301 (permanent) so browsers/proxies + don't cache indefinitely — if the path ever changes again, cached 301s + require manual cache clearing to recover. + """ + return RedirectResponse(url="/setup", status_code=302) @router.get("/admin/tables", response_class=HTMLResponse) diff --git a/app/web/templates/admin_setup_banner.html b/app/web/templates/admin_setup_banner.html index ace18dd..c96d351 100644 --- a/app/web/templates/admin_setup_banner.html +++ b/app/web/templates/admin_setup_banner.html @@ -2,10 +2,18 @@ {% block title %}Setup Banner — {{ config.INSTANCE_NAME }}{% endblock %} {% block content %} - - - - + + + +