Replace mock chart data on reports page with real backend queries (jobs over time, locale stats, usage stats, quality metrics). Add audit logging to auth (login/login_failed), file management (upload/delete TM and reference files), and feedback submission so the system logs page shows complete activity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
63 lines
2.3 KiB
Python
63 lines
2.3 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.auth.schemas import LoginRequest, RefreshRequest, TokenResponse, UserClaims
|
|
from app.auth.service import AuthService
|
|
from app.dependencies import get_current_user, get_db
|
|
from app.services.audit_service import AuditService
|
|
|
|
router = APIRouter(prefix="/auth", tags=["auth"])
|
|
auth_service = AuthService()
|
|
audit_service = AuditService()
|
|
|
|
|
|
@router.post("/login", response_model=TokenResponse)
|
|
async def login(
|
|
body: LoginRequest,
|
|
request: Request,
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> TokenResponse:
|
|
"""Authenticate user and return access + refresh tokens."""
|
|
result = await auth_service.login(body.email, body.password, db)
|
|
if result is None:
|
|
await audit_service.log(
|
|
db, action="login_failed", entity_type="user", entity_id=body.email,
|
|
details={"reason": "Invalid credentials"},
|
|
ip_address=request.client.host if request.client else None,
|
|
)
|
|
await db.commit()
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Invalid email or password",
|
|
)
|
|
# Extract user_id from the access token claims
|
|
claims = auth_service.validate_token(result["access_token"])
|
|
user_id = claims["sub"] if claims else body.email
|
|
await audit_service.log(
|
|
db, action="login", entity_type="user", entity_id=str(user_id),
|
|
user_id=user_id if claims else None,
|
|
details={"email": body.email},
|
|
ip_address=request.client.host if request.client else None,
|
|
)
|
|
await db.commit()
|
|
return TokenResponse(**result)
|
|
|
|
|
|
@router.post("/refresh", response_model=TokenResponse)
|
|
async def refresh_token(body: RefreshRequest) -> TokenResponse:
|
|
"""Exchange a valid refresh token for a new token pair."""
|
|
result = auth_service.refresh_tokens(body.refresh_token)
|
|
if result is None:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Invalid or expired refresh token",
|
|
)
|
|
return TokenResponse(**result)
|
|
|
|
|
|
@router.get("/me", response_model=UserClaims)
|
|
async def get_me(
|
|
current_user: dict = Depends(get_current_user),
|
|
) -> UserClaims:
|
|
"""Return the current authenticated user's claims."""
|
|
return UserClaims(**current_user)
|