from fastapi import APIRouter, Depends, HTTPException, Request from sqlalchemy.orm import Session from typing import List import bcrypt from typing import Optional from pydantic import BaseModel from ..database import get_db from ..models import User, Topic, SystemConfig from ..schemas import UserCreate, UserUpdate, UserResponse from ..core.audit_logger import audit_log from .auth import get_current_admin, org_filter import json router = APIRouter(prefix="/api/admin", tags=["admin"]) @router.get("/users", response_model=List[UserResponse]) def list_users( request: Request, db: Session = Depends(get_db), admin_user: User = Depends(get_current_admin) ): """获取用户列表(管理员)""" q = db.query(User) of = org_filter(admin_user, User) if of is not True: q = q.filter(of) users = q.all() return [UserResponse.from_orm(u) for u in users] @router.post("/users", response_model=UserResponse) def create_user( user_data: UserCreate, request: Request, db: Session = Depends(get_db), admin_user: User = Depends(get_current_admin) ): """创建新用户(管理员)""" # 检查用户名是否已存在 existing = db.query(User).filter(User.username == user_data.username).first() if existing: raise HTTPException(status_code=400, detail="用户名已存在") # bcrypt 密码哈希 hashed_password = bcrypt.hashpw(user_data.password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') user = User( username=user_data.username, password_hash=hashed_password, role=user_data.role or "user", org_id=user_data.org_id or admin_user.org_id or "default" ) db.add(user) db.commit() db.refresh(user) audit_log( action="create_user", user=admin_user, resource_type="user", resource_id=str(user.id), details={"username": user.username, "role": user.role}, ip_address=request.client.host if request.client else None, user_agent=request.headers.get("user-agent", ""), db=db ) return UserResponse.from_orm(user) @router.put("/users/{user_id}", response_model=UserResponse) def update_user( user_id: int, user_update: UserUpdate, request: Request, db: Session = Depends(get_db), admin_user: User = Depends(get_current_admin) ): """更新用户(管理员)""" user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException(status_code=404, detail="用户不存在") if admin_user.role != "admin" and user.org_id != admin_user.org_id: raise HTTPException(status_code=404, detail="用户不存在") changes = {} if user_update.username is not None: if user_update.username != user.username: existing = db.query(User).filter(User.username == user_update.username).first() if existing: raise HTTPException(status_code=400, detail="用户名已存在") changes["username"] = {"old": user.username, "new": user_update.username} user.username = user_update.username if user_update.password is not None: changes["password"] = {"changed": True} user.password_hash = bcrypt.hashpw(user_update.password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') if user_update.role is not None and user_update.role != user.role: changes["role"] = {"old": user.role, "new": user_update.role} user.role = user_update.role db.commit() db.refresh(user) audit_log( action="update_user", user=admin_user, resource_type="user", resource_id=str(user.id), details=changes, ip_address=request.client.host if request.client else None, user_agent=request.headers.get("user-agent", ""), db=db ) return UserResponse.from_orm(user) @router.delete("/users/{user_id}") def delete_user( user_id: int, request: Request, db: Session = Depends(get_db), admin_user: User = Depends(get_current_admin) ): """删除用户(管理员)""" user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException(status_code=404, detail="用户不存在") if admin_user.role != "admin" and user.org_id != admin_user.org_id: raise HTTPException(status_code=404, detail="用户不存在") if user.role == "admin": raise HTTPException(status_code=400, detail="不能删除管理员用户") username = user.username # 保存用于日志 db.delete(user) db.commit() audit_log( action="delete_user", user=admin_user, resource_type="user", resource_id=str(user_id), details={"username": username}, ip_address=request.client.host if request.client else None, user_agent=request.headers.get("user-agent", ""), db=db ) return {"message": "删除成功"} # ---------- Org Management ---------- class OrgCreate(BaseModel): org_id: str name: str description: Optional[str] = None class OrgUpdate(BaseModel): name: Optional[str] = None description: Optional[str] = None @router.get("/orgs") def list_orgs( db: Session = Depends(get_db), admin_user: User = Depends(get_current_admin) ): distinct_orgs = db.query(User.org_id).distinct().all() result = [] for (org_id,) in distinct_orgs: if not org_id: continue cfg = db.query(SystemConfig).filter(SystemConfig.key == f"org:{org_id}").first() meta = json.loads(cfg.value) if cfg and cfg.value else {} user_count = db.query(User).filter(User.org_id == org_id).count() topic_count = db.query(Topic).filter(Topic.org_id == org_id).count() result.append({ "org_id": org_id, "name": meta.get("name", org_id), "description": meta.get("description", ""), "user_count": user_count, "topic_count": topic_count, "is_default": org_id == "default" }) return result @router.post("/orgs") def create_org( data: OrgCreate, request: Request, db: Session = Depends(get_db), admin_user: User = Depends(get_current_admin) ): existing = db.query(SystemConfig).filter(SystemConfig.key == f"org:{data.org_id}").first() if existing: raise HTTPException(status_code=400, detail="组织已存在") cfg = SystemConfig( key=f"org:{data.org_id}", value=json.dumps({"name": data.name, "description": data.description or ""}, ensure_ascii=False), description=f"Organization: {data.org_id}" ) db.add(cfg) db.commit() audit_log( action="create_org", user=admin_user, resource_type="org", resource_id=data.org_id, details=data.model_dump(), ip_address=request.client.host if request.client else None, user_agent=request.headers.get("user-agent", ""), db=db ) return {"org_id": data.org_id, "name": data.name, "description": data.description or ""} @router.put("/orgs/{org_id}") def update_org( org_id: str, data: OrgUpdate, request: Request, db: Session = Depends(get_db), admin_user: User = Depends(get_current_admin) ): cfg = db.query(SystemConfig).filter(SystemConfig.key == f"org:{org_id}").first() if not cfg: raise HTTPException(status_code=404, detail="组织不存在") meta = json.loads(cfg.value) if cfg.value else {} if data.name is not None: meta["name"] = data.name if data.description is not None: meta["description"] = data.description cfg.value = json.dumps(meta, ensure_ascii=False) db.commit() audit_log( action="update_org", user=admin_user, resource_type="org", resource_id=org_id, details=data.model_dump(exclude_unset=True), ip_address=request.client.host if request.client else None, user_agent=request.headers.get("user-agent", ""), db=db ) return {"org_id": org_id, **meta} @router.delete("/orgs/{org_id}") def delete_org( org_id: str, request: Request, db: Session = Depends(get_db), admin_user: User = Depends(get_current_admin) ): if org_id == "default": raise HTTPException(status_code=400, detail="不能删除默认组织") cfg = db.query(SystemConfig).filter(SystemConfig.key == f"org:{org_id}").first() if cfg: db.delete(cfg) db.commit() audit_log( action="delete_org", user=admin_user, resource_type="org", resource_id=org_id, ip_address=request.client.host if request.client else None, user_agent=request.headers.get("user-agent", ""), db=db ) return {"message": "删除成功"}