import secrets from datetime import datetime, timedelta, timezone from fastapi import APIRouter, HTTPException, Request, Response from app.database import get_db from app.models import LoginRequest from app.services.jellyfin import authenticate_user router = APIRouter() SESSION_DURATION_DAYS = 30 async def get_current_user(request: Request) -> dict: """Extract and validate session from cookie. Returns user info or raises 401.""" session_id = request.cookies.get("session_id") if not session_id: raise HTTPException(status_code=401, detail="Not authenticated") db = await get_db() try: cursor = await db.execute( "SELECT user_id, username, jellyfin_token, expires_at FROM sessions WHERE session_id = ?", (session_id,), ) row = await cursor.fetchone() if not row: raise HTTPException(status_code=401, detail="Invalid session") if datetime.fromisoformat(row["expires_at"]) < datetime.now(timezone.utc): await db.execute("DELETE FROM sessions WHERE session_id = ?", (session_id,)) await db.commit() raise HTTPException(status_code=401, detail="Session expired") return { "id": row["user_id"], "name": row["username"], "token": row["jellyfin_token"], } finally: await db.close() @router.post("/login") async def login(request: LoginRequest, response: Response): result = await authenticate_user(request.username, request.password) if not result: raise HTTPException(status_code=401, detail="Invalid username or password") session_id = secrets.token_urlsafe(32) now = datetime.now(timezone.utc) expires = now + timedelta(days=SESSION_DURATION_DAYS) db = await get_db() try: await db.execute( "INSERT INTO sessions (session_id, user_id, username, jellyfin_token, created_at, expires_at) VALUES (?, ?, ?, ?, ?, ?)", (session_id, result["user_id"], result["username"], result["token"], now.isoformat(), expires.isoformat()), ) await db.commit() finally: await db.close() response.set_cookie( key="session_id", value=session_id, httponly=True, samesite="lax", max_age=SESSION_DURATION_DAYS * 86400, ) return {"id": result["user_id"], "name": result["username"]} @router.post("/logout") async def logout(request: Request, response: Response): session_id = request.cookies.get("session_id") if session_id: db = await get_db() try: await db.execute("DELETE FROM sessions WHERE session_id = ?", (session_id,)) await db.commit() finally: await db.close() response.delete_cookie("session_id") return {"ok": True} @router.get("/me") async def me(request: Request): user = await get_current_user(request) return {"id": user["id"], "name": user["name"]}