""" app/api/v1/auth.py — 认证路由 路由层只做:参数校验 → 调 service → 格式化响应(不含业务逻辑) """ import logging from typing import Annotated from fastapi import APIRouter, Depends from pydantic import BaseModel, field_validator from sqlalchemy.orm import Session from app.core.database import get_db from app.core.response import ok, raise_unauthorized from app.core.security import create_access_token, decode_access_token from app.middleware.workspace_guard import CurrentUser, get_current_user from app.models.user import User from app.models.workspace import Workspace logger = logging.getLogger(__name__) router = APIRouter(prefix="/auth", tags=["auth"]) # ── DTO ──────────────────────────────────────────────────── class LoginRequest(BaseModel): username: str password: str @field_validator("username", "password") @classmethod def not_empty(cls, v: str) -> str: if not v or not v.strip(): raise ValueError("不能为空") return v class ChangePasswordRequest(BaseModel): current_password: str new_password: str # ── 路由 ─────────────────────────────────────────────────── @router.post("/login") def login(body: LoginRequest, db: Session = Depends(get_db)): """登录,返回 JWT。密码校验在 service 层(此处调用)。""" from app.services.auth_service import authenticate_user, build_user_response user, workspace_id, role = authenticate_user(db, body.username, body.password) token = create_access_token(user.id, workspace_id, role) return ok({ "token": token, "user": build_user_response(user, workspace_id, role), }) @router.post("/change-password") def change_password( body: ChangePasswordRequest, current_user: Annotated[CurrentUser, Depends(get_current_user)], db: Session = Depends(get_db), ): """修改当前登录用户密码;首登强制改密也走此接口。""" from app.services.auth_service import change_password as do_change_password do_change_password(db, current_user.user_id, body.current_password, body.new_password) return ok({"changed": True}) @router.get("/me") def get_me( current_user: Annotated[CurrentUser, Depends(get_current_user)], db: Session = Depends(get_db), ): """当前用户信息 + workspace + role。""" user = db.query(User).filter(User.id == current_user.user_id).first() if not user: raise_unauthorized("用户不存在") ws = db.query(Workspace).filter(Workspace.id == current_user.workspace_id).first() return ok({ "id": user.id, "username": user.username, "email": user.email, "current_workspace_id": current_user.workspace_id, "role": current_user.role, "must_change_password": bool(getattr(user, "must_change_password", False)), "workspace": { "id": ws.id, "name": ws.name, "slug": ws.slug, } if ws else None, })