2bd3c83cf5
核心功能: - 新增 JWT 认证系统,支持管理员登录/登出 - 前后端合并为单一 FastAPI 应用 (端口 8001) - 系统概览页:6 个统计卡片,点击跳转筛选 - 选题管理页:批量操作 (刷新/创作/优化),时间列展示 - 系统日志页:整合日志查看功能 - 用户管理页:管理员可创建/删除用户 - 移动端适配:响应式布局,底部导航栏 - 标题居中显示 技术改进: - 添加 generated_at 字段支持创作时间记录 - 状态更新时自动同步 updated_at - 所有 API 路由添加 JWT 认证保护 - 前端 authFetch 封装自动附加 Token - 升级 FastAPI 0.136, Pydantic 2.13 等依赖 修复: - 修复 API 500 错误 (数据库列缺失) - 修复 formatRelativeTime 未定义错误 - 修复登录 Token 存储和自动附加逻辑
147 lines
4.8 KiB
Python
147 lines
4.8 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Request
|
|
from sqlalchemy.orm import Session
|
|
from typing import List
|
|
import bcrypt
|
|
|
|
from ..database import get_db
|
|
from ..models import User
|
|
from ..schemas import UserCreate, UserUpdate, UserResponse
|
|
from ..core.audit_logger import audit_log
|
|
|
|
router = APIRouter(prefix="/api/admin", tags=["admin"])
|
|
|
|
def get_current_admin(request: Request, db: Session = Depends(get_db)):
|
|
"""依赖项:验证管理员权限"""
|
|
from .auth import verify_token
|
|
auth_header = request.headers.get("Authorization")
|
|
if not auth_header or not auth_header.startswith("Bearer "):
|
|
raise HTTPException(status_code=401, detail="未提供认证令牌")
|
|
token = auth_header.split(" ")[1]
|
|
user = verify_token(token, db)
|
|
if user.role != "admin":
|
|
raise HTTPException(status_code=403, detail="需要管理员权限")
|
|
return user
|
|
|
|
@router.get("/users", response_model=List[UserResponse])
|
|
def list_users(
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
admin_user: User = Depends(get_current_admin)
|
|
):
|
|
"""获取用户列表(管理员)"""
|
|
users = db.query(User).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"
|
|
)
|
|
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="用户不存在")
|
|
|
|
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 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": "删除成功"}
|