From aecc7c5679eb6a62cdfda9d6800ef8704c3a2b5a Mon Sep 17 00:00:00 2001 From: McElwain Date: Sun, 26 Apr 2026 21:35:15 -0500 Subject: [PATCH] fix: enforce session auth and polish login page --- app/main.py | 30 ++++- app/static/app-shell.css | 21 +++ app/static/app.css | 235 ++++++++++++++++++++++++++++++++++ app/templates/auth/login.html | 6 +- 4 files changed, 283 insertions(+), 9 deletions(-) diff --git a/app/main.py b/app/main.py index 2b60490..befd0f1 100644 --- a/app/main.py +++ b/app/main.py @@ -2,6 +2,7 @@ from pathlib import Path from decimal import Decimal from fastapi import FastAPI, Request +from fastapi.responses import RedirectResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from sqlalchemy import create_engine, func @@ -199,6 +200,29 @@ from starlette.middleware.sessions import SessionMiddleware from app.routes.auth import router as auth_router +@app.middleware("http") +async def auth_session_guard(request, call_next): + session = request.scope.get("session", {}) or {} + current_user = session.get("current_user") + request.state.current_user = current_user + + path = request.url.path + allowed_prefixes = ( + "/login", + "/logout", + "/static", + "/health", + ) + + if path == "/favicon.ico" or any(path.startswith(prefix) for prefix in allowed_prefixes): + return await call_next(request) + + if not current_user: + login_url = f"/login?next={path}" + return RedirectResponse(url=login_url, status_code=303) + + return await call_next(request) + app.add_middleware( SessionMiddleware, secret_key="document-processor-change-this-secret", @@ -208,10 +232,4 @@ app.add_middleware( ) -@app.middleware("http") -async def load_current_user(request, call_next): - session = request.scope.get("session", {}) or {} - request.state.current_user = session.get("current_user") - return await call_next(request) - app.include_router(auth_router) diff --git a/app/static/app-shell.css b/app/static/app-shell.css index 3135685..adb3ae3 100644 --- a/app/static/app-shell.css +++ b/app/static/app-shell.css @@ -948,3 +948,24 @@ body { background: var(--shell-bg); } } } /* ===== end mobile spacing pass ===== */ + + +/* ===== desktop app width fill ===== */ +@media (min-width: 901px) { + .main { + padding-right: 0.6rem !important; + } + + .main-content { + max-width: none !important; + width: calc(100vw - 4.8rem) !important; + margin-right: 0 !important; + padding-right: 0.6rem !important; + } + + .app-shell.nav-open .main-content { + width: calc(100vw - 14.6rem) !important; + } +} +/* ===== end desktop app width fill ===== */ + diff --git a/app/static/app.css b/app/static/app.css index abe381d..7d12064 100644 --- a/app/static/app.css +++ b/app/static/app.css @@ -5460,3 +5460,238 @@ table { } } /* ===== end dashboard mobile polish ===== */ + + +/* ===== desktop detail top card layout v2 ===== */ +@media (min-width: 901px) { + .detail-doc-actions-row { + display: block !important; + margin-bottom: 0.3rem !important; + } + + .detail-doc-actions-grid { + display: grid !important; + grid-template-columns: minmax(20rem, 24rem) auto auto !important; + align-items: end !important; + column-gap: 0.45rem !important; + row-gap: 0.18rem !important; + width: 100% !important; + } + + .detail-doc-type-form { + display: grid !important; + grid-template-columns: minmax(16rem, 1fr) auto !important; + align-items: end !important; + gap: 0.45rem !important; + margin: 0 !important; + min-width: 0 !important; + } + + .detail-type-input-wrap { + min-width: 0 !important; + } + + .detail-type-input-wrap input { + width: 100% !important; + max-width: none !important; + } + + .detail-update-button { + margin: 0 !important; + align-self: end !important; + justify-self: start !important; + } + + .detail-flags-stack { + display: flex !important; + flex-direction: column !important; + justify-content: center !important; + gap: 0.12rem !important; + margin: 0 !important; + align-self: center !important; + } + + .detail-check-inline { + display: inline-flex !important; + align-items: center !important; + gap: 0.25rem !important; + white-space: nowrap !important; + line-height: 1.05 !important; + margin: 0 !important; + } + + .detail-review-flags-form { + margin: 0 !important; + align-self: center !important; + } + + .detail-saveflags-button { + margin: 0 !important; + } + + .detail-save-pdf-form { + display: block !important; + margin: 0 !important; + } + + .detail-path-row { + display: grid !important; + grid-template-columns: minmax(0, 1fr) auto !important; + align-items: end !important; + gap: 0.45rem !important; + width: 100% !important; + } + + .detail-path-row > div:first-child { + min-width: 0 !important; + } + + .detail-save-pdf-form input[name="output_path"] { + width: 100% !important; + min-width: 0 !important; + } + + .queue-nav-row { + display: flex !important; + flex-wrap: wrap !important; + align-items: center !important; + gap: 0.35rem !important; + margin-top: 0.35rem !important; + } + + .queue-nav-row > * { + margin: 0 !important; + } + + .detail-trash-inline, + .detail-trash-form { + order: 3 !important; + } + + .detail-save-document-button { + order: 4 !important; + margin-left: auto !important; + } + + /* right pane title rows should match mobile polish on desktop too */ + .ocr-review-header-row, + .extracted-fields-header-row, + .line-items-header-row { + display: grid !important; + grid-template-columns: minmax(0, 1fr) auto !important; + align-items: center !important; + column-gap: 0.45rem !important; + } +} +/* ===== end desktop detail top card layout v2 ===== */ + + +/* ===== polished login page ===== */ +.login-page-shell { + max-width: 34rem; + margin: 3.5rem auto; + padding: 1.25rem 1rem; +} + +.login-card { + padding: 1.1rem !important; + border-radius: 1.1rem !important; +} + +.login-card .page-title { + margin: 0 0 0.18rem 0 !important; +} + +.login-card .page-subtitle { + margin: 0 0 0.75rem 0 !important; + color: #6b7280 !important; +} + +.login-card .form-grid { + gap: 0.72rem !important; +} + +.login-card .form-field label { + font-size: 0.82rem !important; + margin-bottom: 0.2rem !important; + color: #6b7280 !important; +} + +.login-card input[type="text"], +.login-card input[type="password"] { + min-height: 2.5rem !important; + height: 2.5rem !important; + padding: 0 0.8rem !important; + border-radius: 0.85rem !important; + font-size: 0.98rem !important; +} + +.login-button-row { + margin-top: 0.85rem !important; +} + +.login-button-row button, +.login-button-row .button-link { + min-height: 2.35rem !important; + height: 2.35rem !important; + padding: 0 0.95rem !important; + border-radius: 999px !important; +} + +@media (max-width: 900px) { + .login-page-shell { + max-width: none; + margin: 2.25rem auto; + padding: 0.8rem 0.7rem; + } + + .login-card { + padding: 0.85rem !important; + border-radius: 1rem !important; + } + + .login-card .page-title { + font-size: 1rem !important; + line-height: 1.08 !important; + margin-bottom: 0.12rem !important; + } + + .login-card .page-subtitle { + font-size: 0.72rem !important; + line-height: 1.15 !important; + margin-bottom: 0.55rem !important; + } + + .login-card .form-grid { + gap: 0.5rem !important; + } + + .login-card .form-field label { + font-size: 0.72rem !important; + line-height: 1.1 !important; + margin-bottom: 0.14rem !important; + } + + .login-card input[type="text"], + .login-card input[type="password"] { + min-height: 2.1rem !important; + height: 2.1rem !important; + padding: 0 0.65rem !important; + border-radius: 0.8rem !important; + font-size: 0.88rem !important; + } + + .login-button-row { + margin-top: 0.6rem !important; + } + + .login-button-row button, + .login-button-row .button-link { + min-height: 1.95rem !important; + height: 1.95rem !important; + padding: 0 0.75rem !important; + font-size: 0.8rem !important; + } +} +/* ===== end polished login page ===== */ + diff --git a/app/templates/auth/login.html b/app/templates/auth/login.html index 67d5e7c..0144a2a 100644 --- a/app/templates/auth/login.html +++ b/app/templates/auth/login.html @@ -7,8 +7,8 @@ -
-
+
+
-
+