diff options
| author | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-06 15:25:36 -0700 |
|---|---|---|
| committer | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-06 15:25:36 -0700 |
| commit | 1bdf4ca8c0f51718124ffe5247ac133973d4f251 (patch) | |
| tree | 2904145bdbc5d2a2164cd3cb6c95346e48afd4a5 /main.py | |
| parent | 051775337251b0c7036959901eacb58471100862 (diff) | |
Add authentication, public profile, and infinite scroll
- 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 <noreply@anthropic.com>
Diffstat (limited to 'main.py')
| -rw-r--r-- | main.py | 30 |
1 files changed, 27 insertions, 3 deletions
@@ -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) |
