summaryrefslogtreecommitdiff
path: root/main.py
diff options
context:
space:
mode:
authorTyler Hoang <tyler@tylerhoang.xyz>2026-05-06 15:25:36 -0700
committerTyler Hoang <tyler@tylerhoang.xyz>2026-05-06 15:25:36 -0700
commit1bdf4ca8c0f51718124ffe5247ac133973d4f251 (patch)
tree2904145bdbc5d2a2164cd3cb6c95346e48afd4a5 /main.py
parent051775337251b0c7036959901eacb58471100862 (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.py30
1 files changed, 27 insertions, 3 deletions
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)