260 lines
8.6 KiB
Python
260 lines
8.6 KiB
Python
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": "删除成功"}
|