from __future__ import annotations from collections.abc import AsyncIterator from typing import Annotated from fastapi import Depends, Request from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from sqlalchemy.ext.asyncio import AsyncSession from fintracker.api.errors import Problem from fintracker.api.security import decode_access_token from fintracker.config import Settings, get_settings from fintracker.db import get_sessionmaker from fintracker.models import AppUser _bearer = HTTPBearer(auto_error=False) async def get_session() -> AsyncIterator[AsyncSession]: async with get_sessionmaker()() as session: yield session SessionDep = Annotated[AsyncSession, Depends(get_session)] SettingsDep = Annotated[Settings, Depends(get_settings)] async def get_current_user( session: SessionDep, settings: SettingsDep, creds: Annotated[HTTPAuthorizationCredentials | None, Depends(_bearer)], ) -> AppUser: if creds is None: raise Problem(401, "Unauthorized", "Missing bearer token") user_id = decode_access_token(creds.credentials, settings) if user_id is None: raise Problem(401, "Unauthorized", "Invalid or expired token") user = await session.get(AppUser, user_id) if user is None: raise Problem(401, "Unauthorized", "Unknown user") return user CurrentUser = Annotated[AppUser, Depends(get_current_user)] def client_ip(request: Request) -> str: fwd = request.headers.get("x-forwarded-for") if fwd: return fwd.split(",")[0].strip() return request.client.host if request.client else "unknown"