Files
lifetrack/apps/api/app/modules/auth/router.py
T
MeeJayandClaude Opus 5 93f0689c1e Initial import: LifeTrack v1 (santé, vape, finances)
Tracker de vie auto-hébergé : suivi poids/calories/sport avec planning de
pesées, sevrage tabac (vape) avec modèle de coût DIY et économies, et
finances personnelles avec import de relevés bancaires.

Architecture : FastAPI + SQLAlchemy 2.0 + PostgreSQL 16, React 18 + TS +
Vite + Tailwind + ECharts, déploiement Docker Compose. Modules
auto-découverts des deux côtés (pkgutil / import.meta.glob) et framework
de connecteurs à deux voies (importeurs de fichiers + ingestion JSON)
pour brancher de nouvelles sources sans toucher au noyau.

Validé : 292 tests pytest, tsc + vite build, contrat API/web vérifié
contre le schéma OpenAPI, et déploiement Docker réel sur PostgreSQL 16
(28 tables, SPA servie par nginx, wizard de premier démarrage).

Documentation : README.md, docs/GUIDE.md, CONVENTIONS.md, et les
documents de conception et de recherche dans docs/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:48:57 +02:00

81 lines
2.4 KiB
Python

from typing import Annotated
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.dependencies import get_current_user
from app.core.security import create_access_token
from app.modules.auth import service
from app.modules.auth.models import User
from app.modules.auth.schemas import (
AuthStatus,
DeviceKeyCreate,
DeviceKeyCreated,
DeviceKeyRead,
LoginRequest,
SetupRequest,
TokenResponse,
UserRead,
UserUpdate,
)
router = APIRouter(prefix="/auth", tags=["auth"])
DbDep = Annotated[Session, Depends(get_db)]
UserDep = Annotated[User, Depends(get_current_user)]
def _token_response(user: User) -> TokenResponse:
return TokenResponse(
access_token=create_access_token(user.id),
user=UserRead.model_validate(user),
)
@router.get("/status", response_model=AuthStatus)
def auth_status(db: DbDep) -> AuthStatus:
required = service.setup_required(db)
return AuthStatus(setup_required=required, needs_setup=required)
@router.post("/setup", response_model=TokenResponse, status_code=201)
def setup(payload: SetupRequest, db: DbDep) -> TokenResponse:
user = service.create_first_user(db, payload)
return _token_response(user)
@router.post("/login", response_model=TokenResponse)
def login(payload: LoginRequest, db: DbDep) -> TokenResponse:
user = service.authenticate(db, payload.email, payload.password)
return _token_response(user)
@router.get("/me", response_model=UserRead)
def me(user: UserDep) -> User:
return user
@router.patch("/me", response_model=UserRead)
def update_me(payload: UserUpdate, db: DbDep, user: UserDep) -> User:
return service.update_me(db, user, payload)
@router.get("/device-keys", response_model=list[DeviceKeyRead])
def list_device_keys(db: DbDep, user: UserDep) -> list[DeviceKeyRead]:
return service.list_device_keys(db, user.id)
@router.post("/device-keys", response_model=DeviceKeyCreated, status_code=201)
def create_device_key(
payload: DeviceKeyCreate, db: DbDep, user: UserDep
) -> DeviceKeyCreated:
key, plaintext = service.create_device_key(db, user.id, payload)
read = DeviceKeyRead.model_validate(key)
return DeviceKeyCreated(**read.model_dump(), key=plaintext)
@router.delete("/device-keys/{key_id}", status_code=204)
def revoke_device_key(key_id: int, db: DbDep, user: UserDep) -> None:
service.revoke_device_key(db, user.id, key_id)