from datetime import datetime from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select from sqlalchemy.orm import Session from app.api.deps import CurrentUser from app.core.security import create_access_token, create_refresh_token, verify_password from app.db.session import get_db from app.models.domain import User from app.schemas.domain import LoginRequest, TokenPair, UserRead from app.services.audit import write_audit router = APIRouter(prefix="/auth", tags=["auth"]) @router.post("/login", response_model=TokenPair) def login(payload: LoginRequest, db: Session = Depends(get_db)) -> TokenPair: user = db.scalar(select(User).where(User.email == payload.email)) if not user or not verify_password(payload.password, user.password_hash): if user: user.failed_login_attempts += 1 db.commit() raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials") permissions = sorted({permission for role in user.roles for permission in role.permissions}) user.failed_login_attempts = 0 db.commit() write_audit(db, action="login", object_type="user", object_id=user.id, user_id=user.id) return TokenPair( access_token=create_access_token(user.id, permissions), refresh_token=create_refresh_token(user.id), ) @router.get("/me", response_model=UserRead) def me(user: CurrentUser) -> User: return user @router.post("/logout") def logout(user: CurrentUser, db: Session = Depends(get_db)) -> dict[str, str]: write_audit(db, action="logout", object_type="user", object_id=user.id, user_id=user.id) return {"status": "ok", "at": datetime.utcnow().isoformat()}