"""Casdoor 认证 API""" from datetime import datetime from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from pydantic import BaseModel from app.core.deps import get_db from app.core.auth import create_access_token, exchange_code_for_user, get_current_user from app.models.user import User, UserRoleEnum router = APIRouter(prefix="/api/auth", tags=["认证"]) class LoginRequest(BaseModel): code: str # Casdoor 返回的 OAuth code class LoginResponse(BaseModel): token: str user: dict @router.post("/login", response_model=LoginResponse) async def login(data: LoginRequest, db: AsyncSession = Depends(get_db)): """用 Casdoor OAuth code 登录""" casdoor_user = await exchange_code_for_user(data.code) if not casdoor_user: raise HTTPException(status_code=401, detail="Casdoor 认证失败") # 从 id_token 中提取用户信息 # id_token payload 一般包含: sub, name, preferred_username, email, displayName 等 casdoor_uid = casdoor_user.get("sub") or casdoor_user.get("name", "") username = casdoor_user.get("preferred_username") or casdoor_user.get("name", casdoor_uid) display_name = casdoor_user.get("displayName", "") if not casdoor_uid: # 尝试用 name 作为备用标识 casdoor_uid = casdoor_user.get("name", "") if not casdoor_uid: raise HTTPException(status_code=401, detail="无法从 Casdoor 获取用户标识") # 查找或创建本地用户 result = await db.execute(select(User).where(User.casdoor_uid == casdoor_uid)) user = result.scalar_one_or_none() if not user: # 首次登录,自动创建 viewer 账号 user = User( casdoor_uid=casdoor_uid, username=username, display_name=display_name or username, role=UserRoleEnum.viewer, ) db.add(user) await db.commit() await db.refresh(user) logger.info(f"新用户自动创建: {username} (uid: {casdoor_uid})") # 更新最后登录时间 user.last_login_at = datetime.now() await db.commit() # 签发 PingWatch JWT token = create_access_token( data={"sub": user.casdoor_uid, "role": user.role.value} ) return LoginResponse( token=token, user={ "id": user.id, "username": user.username, "display_name": user.display_name, "role": user.role.value, }, ) @router.get("/me") async def get_me(current_user: User = Depends(get_current_user)): """获取当前登录用户信息""" return { "id": current_user.id, "username": current_user.username, "display_name": current_user.display_name, "role": current_user.role.value, }