"""认证相关路由。 提供: - 登录获取 Bearer Token - 系统首次初始化(bootstrap:创建首个管理员) - 获取当前登录用户信息(/me) """ from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker from backend.api.schemas import BootstrapRequest, BootstrapResponse, LoginRequest, LoginResponse, MeResponse, TokenResponse, UserResponse from backend.auth.deps import get_current_user from backend.database.schema import sys_user from backend.services.auth_service import AuthService from backend.services.user_service import UserService def get_router(session_factory: async_sessionmaker[AsyncSession]) -> APIRouter: """构造认证路由。""" router = APIRouter(prefix="/api/auth", tags=["auth"]) auth = AuthService(session_factory) users = UserService(session_factory) @router.post("/login", response_model=LoginResponse) async def login(body: LoginRequest) -> LoginResponse: """用户名密码登录,返回 access_token。""" try: token, user_row = await auth.login(username=body.username, password=body.password, expires_in_seconds=3600) except PermissionError: raise HTTPException(status_code=403, detail="inactive user") except ValueError: raise HTTPException(status_code=401, detail="invalid credentials") user = await users.get_user(str(user_row["user_id"])) if not user: raise HTTPException(status_code=401, detail="invalid credentials") return LoginResponse(token=TokenResponse(access_token=token, expires_in=3600), user=UserResponse(**user)) @router.post("/bootstrap", response_model=BootstrapResponse) async def bootstrap(body: BootstrapRequest) -> BootstrapResponse: """初始化系统:当系统还没有任何用户时,创建首个管理员并返回 token。""" async with session_factory() as session: exists = (await session.execute(select(sys_user.c.user_id).limit(1))).first() if exists: raise HTTPException(status_code=409, detail="already initialized") try: created = await users.create_user( user_id=None, username=body.username, display_name=body.display_name, password=body.password, role_id="admin", is_active=True, extra=None, ) except Exception: raise HTTPException(status_code=400, detail="bootstrap failed") token, _ = await auth.login(username=body.username, password=body.password, expires_in_seconds=3600) return BootstrapResponse(token=TokenResponse(access_token=token, expires_in=3600), user=UserResponse(**created)) @router.get("/me", response_model=MeResponse) async def me(current_user: dict = Depends(get_current_user(session_factory))) -> MeResponse: """返回当前登录用户(由 Authorization: Bearer token 解析)。""" user = UserResponse( user_id=str(current_user["user_id"]), username=str(current_user["username"]), display_name=current_user.get("display_name"), role_id=str(current_user["role_id"]), role_name=current_user.get("role_name"), is_active=bool(current_user.get("is_active")), last_login_at=current_user.get("last_login_at"), created_at=current_user.get("created_at"), updated_at=current_user.get("updated_at"), extra=current_user.get("extra"), ) return MeResponse(user=user) return router