""" FastAPI dependencies for authentication and user-scoped data access. """ from typing import Optional from fastapi import Depends, HTTPException, Query, Request, status from fastapi.security import OAuth2PasswordBearer from jose import JWTError from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload from app.auth import decode_token from app.database import get_db from app.models.user import User from app.models.photos import Photo from app.models.folders import Folder, SourceRoot from app.models.heaps import Heap, heap_photos from app.models.tags import Tag from app.models.sharing import HeapShare, FolderShare oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login") async def get_current_user( token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db), ) -> User: """Decode JWT, look up user, raise 401 if invalid or inactive.""" credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired token", headers={"WWW-Authenticate": "Bearer"}, ) try: payload = decode_token(token) user_id: str = payload.get("sub") token_type: str = payload.get("type") if user_id is None or token_type != "access": raise credentials_exception except JWTError: raise credentials_exception result = await db.execute(select(User).where(User.id == user_id)) user = result.scalar_one_or_none() if user is None or not user.is_active: raise credentials_exception return user async def get_current_user_media( request: Request, token: Optional[str] = Query(None, alias="token"), db: AsyncSession = Depends(get_db), ) -> User: """Authenticate via Authorization header OR ?token= query parameter. Used for media endpoints (thumbnails, originals, proxies) where the URL is set as an or