Files
NexaPG/backend/app/api/routes/auth.py
nessi 117710cc0a [NX-101 Issue] Refactor error handling to use consistent API error format
Replaced all inline error messages with the standardized `api_error` helper for consistent error response formatting. This improves clarity, maintainability, and ensures uniform error structures across the application. Updated logging for collector failures to include error class and switched to warning level for target unreachable scenarios.
2026-02-14 11:30:56 +01:00

68 lines
2.9 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, status
import 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.errors import api_error
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=api_error("invalid_credentials", "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 jwt.InvalidTokenError as exc:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=api_error("invalid_refresh_token", "Invalid refresh token"),
) from exc
if token_payload.get("type") != "refresh":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=api_error("invalid_refresh_token_type", "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=api_error("user_not_found", "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)