Files
fin-tracker/backend/src/fintracker/api/deps.py
T
Dmitry 3fc7a954b9 feat(api): FastAPI — auth, RFC 7807 и роуты фазы 1
Префикс /api/v1, operationId = "<tag>_<name>", чтобы Dart-клиент получил методы
вроде accountsList, а не list_accounts_api_v1_accounts_get. Ошибки —
application/problem+json. Auth: access-JWT на час + refresh на 30 дней, который
хранится хэшем и ротируется, логин ограничен по частоте в памяти.

Деньги в JSON — всегда позиционные строки (api/schemas/common.py): float в
проводе потерял бы копейки, которые NUMERIC(24,10) бережёт.

Тестовый harness поднимает свой Postgres через pg_ctl (pytest-postgresql),
мигрирует его один раз на сессию и усекает таблицы после каждого теста.
2026-09-18 13:43:49 +03:00

52 lines
1.6 KiB
Python

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"