from fastapi import APIRouter, Depends, HTTPException, status from jose import JWTError, jwt from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.config import get_settings from app.core.db import get_db from app.core.deps import get_current_user from app.core.security import create_access_token, create_refresh_token, verify_password from app.models.models import User from app.schemas.auth import LoginRequest, RefreshRequest, TokenResponse from app.schemas.user import UserOut from app.services.audit import write_audit_log router = APIRouter() settings = get_settings() @router.post("/login", response_model=TokenResponse) async def login(payload: LoginRequest, db: AsyncSession = Depends(get_db)) -> TokenResponse: user = await db.scalar(select(User).where(User.email == payload.email)) if not user or not verify_password(payload.password, user.password_hash): raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials") await write_audit_log(db, action="auth.login", user_id=user.id, payload={"email": user.email}) return TokenResponse(access_token=create_access_token(str(user.id)), refresh_token=create_refresh_token(str(user.id))) @router.post("/refresh", response_model=TokenResponse) async def refresh(payload: RefreshRequest, db: AsyncSession = Depends(get_db)) -> TokenResponse: try: token_payload = jwt.decode(payload.refresh_token, settings.jwt_secret_key, algorithms=[settings.jwt_algorithm]) except JWTError as exc: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid refresh token") from exc if token_payload.get("type") != "refresh": raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid refresh token type") user_id = token_payload.get("sub") user = await db.scalar(select(User).where(User.id == int(user_id))) if not user: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found") await write_audit_log(db, action="auth.refresh", user_id=user.id, payload={}) return TokenResponse(access_token=create_access_token(str(user.id)), refresh_token=create_refresh_token(str(user.id))) @router.post("/logout") async def logout(user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)) -> dict: await write_audit_log(db, action="auth.logout", user_id=user.id, payload={}) return {"status": "ok"} @router.get("/me", response_model=UserOut) async def me(user: User = Depends(get_current_user)) -> UserOut: return UserOut.model_validate(user)