From 1bdf4ca8c0f51718124ffe5247ac133973d4f251 Mon Sep 17 00:00:00 2001 From: Tyler Hoang Date: Wed, 6 May 2026 15:25:36 -0700 Subject: Add authentication, public profile, and infinite scroll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implement session-based auth with argon2 password hashing - Add login form and logout button in nav - Create public /tyler profile page with curated stats - Implement infinite scroll for film lists (load 20 at a time) - Add lazy loading for poster images - Fix profile page CSS to use dark theme variables - Use consistent star character (✦) across all pages - Add /films/partial endpoint for pagination Co-Authored-By: Claude Haiku 4.5 --- main.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'main.py') diff --git a/main.py b/main.py index 257d2b6..b04ad96 100644 --- a/main.py +++ b/main.py @@ -1,15 +1,33 @@ +import os from contextlib import asynccontextmanager from dotenv import load_dotenv -from fastapi import FastAPI +from fastapi import FastAPI, Request +from fastapi.responses import RedirectResponse from fastapi.staticfiles import StaticFiles +from starlette.middleware.sessions import SessionMiddleware +from starlette.middleware.base import BaseHTTPMiddleware from database import init_db -from routers import films, imports as imports_router, stats, tmdb +from routers import auth, films, imports as imports_router, profile, stats, tmdb load_dotenv() +class AuthMiddleware(BaseHTTPMiddleware): + async def dispatch(self, request: Request, call_next): + public_paths = {"/login", "/logout", "/tyler"} + path = request.url.path + + if path.startswith("/static") or path in public_paths: + return await call_next(request) + + if not request.session.get("authenticated"): + return RedirectResponse("/login", status_code=303) + + return await call_next(request) + + @asynccontextmanager async def lifespan(app: FastAPI): init_db() @@ -18,8 +36,14 @@ async def lifespan(app: FastAPI): app = FastAPI(title="Lumière", lifespan=lifespan) -app.mount("/static", StaticFiles(directory="static"), name="static") +# Middleware order: SessionMiddleware first, then AuthMiddleware +session_secret = os.getenv("SESSION_SECRET", "change-me-in-production") +app.add_middleware(AuthMiddleware) +app.add_middleware(SessionMiddleware, secret_key=session_secret) +app.mount("/static", StaticFiles(directory="static"), name="static") +app.include_router(auth.router) +app.include_router(profile.router) app.include_router(tmdb.router) app.include_router(imports_router.router) app.include_router(stats.router) -- cgit v1.3-2-g0d8e