from datetime import datetime, timezone from fastapi import APIRouter, Depends, HTTPException, Request, status from fastapi.security import OAuth2PasswordRequestForm from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from pydantic import BaseModel from app.core.database import get_db from app.core.security import verify_password, create_access_token, hash_password from app.core.deps import get_current_user from app.models.user import User from app.services.audit import log_audit router = APIRouter(prefix="/auth", tags=["认证"]) class Token(BaseModel): access_token: str token_type: str = "bearer" user: dict class RegisterRequest(BaseModel): username: str password: str full_name: str | None = None email: str | None = None phone: str | None = None @router.post("/login", response_model=Token) async def login(request: Request, form: OAuth2PasswordRequestForm = Depends(), db: AsyncSession = Depends(get_db)): result = await db.execute(select(User).where(User.username == form.username)) user = result.scalar_one_or_none() if not user or not verify_password(form.password, user.hashed_password): raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="用户名或密码错误") if not user.is_active: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="账号已禁用") user.last_login = datetime.now(timezone.utc) token = create_access_token({"sub": str(user.id), "role": user.role}) client_ip = request.client.host if request.client else None await log_audit(db, user.id, "login", "auth", detail=f"用户 {user.username} 登录", ip_address=client_ip) return Token( access_token=token, user={"id": user.id, "username": user.username, "full_name": user.full_name, "role": user.role} ) @router.get("/me") async def get_me(user: User = Depends(get_current_user)): return { "id": user.id, "username": user.username, "full_name": user.full_name, "email": user.email, "phone": user.phone, "role": user.role, "is_active": user.is_active, }