from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from app.crud import crud_refresh_token, crud_user from app.database import get_db from app.dependencies import get_current_user from app.models.user import User from app.schemas.auth import LoginRequest, RefreshRequest, RegisterRequest, TokenResponse, UserOut from app.services.auth_service import ( create_access_token, create_refresh_token, hash_token, verify_password, ) router = APIRouter(prefix="/auth", tags=["auth"]) @router.post("/register", response_model=TokenResponse, status_code=status.HTTP_201_CREATED) async def register(body: RegisterRequest, db: AsyncSession = Depends(get_db)): if await crud_user.get_by_email(db, body.email): raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Email already registered" ) user = await crud_user.create( db, email=body.email, password=body.password, full_name=body.full_name, phone=body.phone, ) return await _issue_tokens(db, user) @router.post("/login", response_model=TokenResponse) async def login(body: LoginRequest, db: AsyncSession = Depends(get_db)): user = await crud_user.get_by_email(db, body.email) if not user or not verify_password(body.password, user.hashed_password): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials" ) return await _issue_tokens(db, user) @router.post("/refresh", response_model=TokenResponse) async def refresh(body: RefreshRequest, db: AsyncSession = Depends(get_db)): hashed = hash_token(body.refresh_token) rt = await crud_refresh_token.get_by_hash(db, hashed) if not rt or not crud_refresh_token.is_valid(rt): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired refresh token" ) await crud_refresh_token.revoke(db, rt) user = await crud_user.get(db, rt.user_id) if not user: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED) return await _issue_tokens(db, user) @router.post("/logout", status_code=status.HTTP_204_NO_CONTENT) async def logout(body: RefreshRequest, db: AsyncSession = Depends(get_db)): hashed = hash_token(body.refresh_token) rt = await crud_refresh_token.get_by_hash(db, hashed) if rt: await crud_refresh_token.revoke(db, rt) @router.get("/me", response_model=UserOut) async def me(user: User = Depends(get_current_user)): return user async def _issue_tokens(db: AsyncSession, user: User) -> TokenResponse: access = create_access_token(str(user.id), user.role, user.status) raw_rt, hashed_rt, expires_at = create_refresh_token() await crud_refresh_token.create(db, user.id, hashed_rt, expires_at) return TokenResponse(access_token=access, refresh_token=raw_rt)