from fastapi import APIRouter, Depends from sqlalchemy.ext.asyncio import AsyncSession from app.core.database import get_db from app.core.dependencies import get_current_user from app.datamodels.schemas import ( ForgotPasswordRequest, LoginRequest, MessageResponse, RefreshRequest, RegisterRequest, ResetPasswordRequest, TokenResponse, UserResponse, ) from app.integrations.sub2api import admin as sub2api_admin from app.integrations.sub2api.client import Sub2APIError, Sub2APITransportError from app.models import User from app.services.auth_service import AuthService router = APIRouter(prefix="/auth", tags=["auth"]) @router.post("/register", response_model=UserResponse) async def register(body: RegisterRequest, db: AsyncSession = Depends(get_db)): user = await AuthService.register(db, body.email, body.password) return _user_to_response(user) @router.post("/login", response_model=TokenResponse) async def login(body: LoginRequest, db: AsyncSession = Depends(get_db)): return await AuthService.login(db, body.email, body.password) @router.post("/refresh", response_model=TokenResponse) async def refresh(body: RefreshRequest, db: AsyncSession = Depends(get_db)): return await AuthService.refresh(db, body.refresh_token) @router.post("/logout", response_model=MessageResponse) async def logout(): return {"message": "Logged out successfully"} @router.post("/forgot-password", response_model=MessageResponse) async def forgot_password(body: ForgotPasswordRequest, db: AsyncSession = Depends(get_db)): token = await AuthService.forgot_password(db, body.email) if token: # MVP: print to console; production should send via email print(f"[Password Reset] email={body.email} token={token}") return {"message": "If the email exists, a reset link has been sent"} @router.post("/reset-password", response_model=MessageResponse) async def reset_password(body: ResetPasswordRequest, db: AsyncSession = Depends(get_db)): await AuthService.reset_password(db, body.token, body.new_password) return {"message": "Password reset successfully"} @router.get("/me", response_model=UserResponse) async def me(user: User = Depends(get_current_user)): """Merge the local row with sub2api's live balance when available.""" response = _user_to_response(user) if user.sub2api_user_id: try: remote = await sub2api_admin.get_user(user.sub2api_user_id) response.balance = float(remote.get("balance") or 0) except (Sub2APIError, Sub2APITransportError): # non-fatal; fall back to 0 pass return response def _user_to_response(user: User) -> UserResponse: return UserResponse( id=user.id, email=user.email, status=user.status, created_at=user.created_at, sub2api_user_id=user.sub2api_user_id, balance=0.0, )