from sqlalchemy.ext.asyncio import AsyncSession from fastapi import Depends from fastapi.security import OAuth2PasswordBearer from typing import Callable from datetime import datetime from fooder.db import get_db_session from fooder.domain import User from fooder.repository import Repository from fooder.utils.datetime import utc_now from fooder.utils.jwt import AccessToken from fooder.exc import Unauthorized class Context: """ Main API context, aggregating dependencies """ def __init__( self, repo: Repository, clock: Callable[[], datetime] = utc_now, _user: User | None = None, ) -> None: self.repo = repo self.clock = clock self._user = _user @property def user(self) -> User: if self._user is None: raise Unauthorized() return self._user class ContextDependency: """ Context dependecy """ def __init__( self, ) -> None: pass def __call__( self, session: AsyncSession = Depends(get_db_session), ): return Context(repo=Repository(session)) class AuthContextDependency: """ Context dependecy for authorized endpionts """ async def __call__( self, token: str = Depends(OAuth2PasswordBearer(tokenUrl="/token")), session: AsyncSession = Depends(get_db_session), ) -> Context: access_token = AccessToken.decode(token) repo = Repository(session) user = await repo.user.get(User.id == access_token.sub) if user is None: raise Unauthorized() return Context(repo=repo, _user=user)