初步增加 web 界面
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,152 @@
|
||||
"""账号管理路由"""
|
||||
|
||||
import re
|
||||
import csv
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ..database import get_db
|
||||
from ..models import User, Account, AuditLog
|
||||
from ..schemas import AccountImport, AccountAssign, AccountOut
|
||||
from ..deps import get_current_user, require_permission
|
||||
from ..permissions import has_permission
|
||||
|
||||
router = APIRouter(prefix="/api/accounts", tags=["账号管理"])
|
||||
|
||||
EMAIL_PATTERN = re.compile(r'^[^\s@|]+@[^\s@|]+\.[^\s@|]+$')
|
||||
|
||||
|
||||
def _split_account_line(line: str) -> list[str]:
|
||||
if '|' in line:
|
||||
return line.split('|')
|
||||
if '\t' in line:
|
||||
return line.split('\t')
|
||||
if ',' in line:
|
||||
return next(csv.reader([line]))
|
||||
return line.split()
|
||||
|
||||
|
||||
@router.get("", response_model=list[AccountOut])
|
||||
def list_accounts(
|
||||
assigned_only: bool = Query(False),
|
||||
db: Session = Depends(get_db),
|
||||
current: User = Depends(get_current_user),
|
||||
):
|
||||
"""列表:按角色返回不同字段和范围。"""
|
||||
query = db.query(Account)
|
||||
|
||||
# 权限控制:客服只能看分配给自己的
|
||||
if not has_permission(current.role, "account:view_all"):
|
||||
if has_permission(current.role, "account:view_assigned"):
|
||||
query = query.filter(Account.assigned_to == current.id)
|
||||
else:
|
||||
raise HTTPException(status_code=403, detail="无权查看账号")
|
||||
|
||||
if assigned_only and has_permission(current.role, "account:view_all"):
|
||||
query = query.filter(Account.assigned_to.isnot(None))
|
||||
|
||||
accounts = query.order_by(Account.id).all()
|
||||
result = []
|
||||
for acc in accounts:
|
||||
item = AccountOut(
|
||||
id=acc.id, username=acc.username, remark=acc.remark or "",
|
||||
assigned_to=acc.assigned_to,
|
||||
assigned_username=acc.assigned_user.username if acc.assigned_user else None,
|
||||
created_at=acc.created_at,
|
||||
)
|
||||
# 运营+超管可看完整字段
|
||||
if has_permission(current.role, "account:view_all"):
|
||||
item.password = acc.password
|
||||
item.email = acc.email
|
||||
item.email_password = acc.email_password
|
||||
result.append(item)
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/import")
|
||||
def import_accounts(
|
||||
req: AccountImport,
|
||||
db: Session = Depends(get_db),
|
||||
current: User = Depends(require_permission("account:import")),
|
||||
):
|
||||
"""批量导入账号。格式:用户名|密码|邮箱|邮箱密码"""
|
||||
from douyu.email_verifier import get_email_config_for_account
|
||||
|
||||
accounts = []
|
||||
skipped = 0
|
||||
for line_num, line in enumerate(req.text.strip().split('\n'), 1):
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
parts = _split_account_line(line)
|
||||
if len(parts) != 4:
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
username, password, email, email_password = [p.strip() for p in parts]
|
||||
if not all([username, password, email, email_password]):
|
||||
skipped += 1
|
||||
continue
|
||||
if not EMAIL_PATTERN.match(email):
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
email_cfg = get_email_config_for_account(email)
|
||||
accounts.append(Account(
|
||||
username=username,
|
||||
password=password,
|
||||
email=email,
|
||||
email_password=email_password,
|
||||
email_imap_server=email_cfg['server'],
|
||||
email_imap_port=email_cfg.get('port', 993),
|
||||
))
|
||||
|
||||
if accounts:
|
||||
db.add_all(accounts)
|
||||
db.add(AuditLog(user_id=current.id, username=current.username,
|
||||
action="account:import", target=f"导入{len(accounts)}个"))
|
||||
db.commit()
|
||||
|
||||
return {"message": f"导入成功 {len(accounts)} 个,跳过 {skipped} 个", "success": True, "count": len(accounts)}
|
||||
|
||||
|
||||
@router.put("/{account_id}/assign")
|
||||
def assign_account(
|
||||
account_id: int,
|
||||
req: AccountAssign,
|
||||
db: Session = Depends(get_db),
|
||||
current: User = Depends(require_permission("account:assign")),
|
||||
):
|
||||
acc = db.query(Account).filter(Account.id == account_id).first()
|
||||
if not acc:
|
||||
raise HTTPException(status_code=404, detail="账号不存在")
|
||||
|
||||
if req.assigned_to:
|
||||
target = db.query(User).filter(User.id == req.assigned_to).first()
|
||||
if not target:
|
||||
raise HTTPException(status_code=404, detail="目标用户不存在")
|
||||
if target.role != "support":
|
||||
raise HTTPException(status_code=400, detail="只能分配给客服角色")
|
||||
|
||||
acc.assigned_to = req.assigned_to
|
||||
db.add(AuditLog(user_id=current.id, username=current.username,
|
||||
action="account:assign", target=acc.username))
|
||||
db.commit()
|
||||
return {"message": "已分配", "success": True}
|
||||
|
||||
|
||||
@router.delete("/{account_id}")
|
||||
def delete_account(
|
||||
account_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current: User = Depends(require_permission("account:delete")),
|
||||
):
|
||||
acc = db.query(Account).filter(Account.id == account_id).first()
|
||||
if not acc:
|
||||
raise HTTPException(status_code=404, detail="账号不存在")
|
||||
|
||||
db.add(AuditLog(user_id=current.id, username=current.username,
|
||||
action="account:delete", target=acc.username))
|
||||
db.delete(acc)
|
||||
db.commit()
|
||||
return {"message": "已删除", "success": True}
|
||||
@@ -0,0 +1,57 @@
|
||||
"""认证路由"""
|
||||
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ..database import get_db
|
||||
from ..models import User, AuditLog
|
||||
from ..security import verify_password, create_access_token
|
||||
from ..permissions import get_role_permissions, ROLE_LABELS
|
||||
from ..schemas import LoginRequest, TokenResponse
|
||||
from ..deps import get_current_user
|
||||
|
||||
router = APIRouter(prefix="/api/auth", tags=["认证"])
|
||||
|
||||
|
||||
@router.post("/login", response_model=TokenResponse)
|
||||
def login(req: LoginRequest, db: Session = Depends(get_db)):
|
||||
user = db.query(User).filter(User.username == req.username).first()
|
||||
if not user or not verify_password(req.password, user.password_hash):
|
||||
raise HTTPException(status_code=401, detail="用户名或密码错误")
|
||||
if not user.is_active:
|
||||
raise HTTPException(status_code=403, detail="账号已禁用,请联系管理员")
|
||||
|
||||
token = create_access_token({"sub": str(user.id), "role": user.role})
|
||||
perms = get_role_permissions(user.role)
|
||||
|
||||
# 审计
|
||||
db.add(AuditLog(user_id=user.id, username=user.username, action="login", target="auth"))
|
||||
db.commit()
|
||||
|
||||
return TokenResponse(
|
||||
access_token=token,
|
||||
role=user.role,
|
||||
username=user.username,
|
||||
permissions=perms,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/me")
|
||||
def me(current_user: User = Depends(get_current_user)):
|
||||
return {
|
||||
"id": current_user.id,
|
||||
"username": current_user.username,
|
||||
"role": current_user.role,
|
||||
"role_label": ROLE_LABELS.get(current_user.role, current_user.role),
|
||||
"is_active": current_user.is_active,
|
||||
"remark": current_user.remark,
|
||||
"permissions": get_role_permissions(current_user.role),
|
||||
}
|
||||
|
||||
|
||||
@router.post("/logout")
|
||||
def logout(current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
db.add(AuditLog(user_id=current_user.id, username=current_user.username, action="logout", target="auth"))
|
||||
db.commit()
|
||||
return {"message": "已登出"}
|
||||
@@ -0,0 +1,137 @@
|
||||
"""登录任务路由 + WebSocket 实时日志"""
|
||||
|
||||
import asyncio
|
||||
import threading
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, HTTPException, WebSocket, WebSocketDisconnect
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ..database import get_db, SessionLocal
|
||||
from ..models import User, Account, LoginTask, ProxyConfig as ProxyConfigModel
|
||||
from ..schemas import LoginBatchRequest, LoginTaskOut
|
||||
from ..deps import get_current_user, require_permission
|
||||
from ..permissions import has_permission
|
||||
from ..services.login_service import LoginBatchRunner
|
||||
|
||||
router = APIRouter(prefix="/api/login", tags=["登录任务"])
|
||||
|
||||
# 运行中的批次: batch_id -> {runner, log_queue, loop}
|
||||
_active_batches: dict[str, dict] = {}
|
||||
|
||||
|
||||
@router.post("/batch")
|
||||
def create_batch(
|
||||
req: LoginBatchRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current: User = Depends(require_permission("login:batch")),
|
||||
):
|
||||
"""创建批量登录任务。"""
|
||||
if not req.account_ids:
|
||||
raise HTTPException(status_code=400, detail="请选择账号")
|
||||
|
||||
# 读取代理配置
|
||||
proxy = db.query(ProxyConfigModel).first()
|
||||
|
||||
# 权限过滤账号
|
||||
valid_ids = []
|
||||
for aid in req.account_ids:
|
||||
acc = db.query(Account).filter(Account.id == aid).first()
|
||||
if not acc:
|
||||
continue
|
||||
if not has_permission(current.role, "login:view_all"):
|
||||
if acc.assigned_to != current.id:
|
||||
continue
|
||||
valid_ids.append(aid)
|
||||
|
||||
if not valid_ids:
|
||||
raise HTTPException(status_code=403, detail="没有可登录的账号")
|
||||
|
||||
# 创建执行器(用独立的 DB 会话,因为在线程中运行)
|
||||
thread_db = SessionLocal()
|
||||
runner = LoginBatchRunner(
|
||||
db=thread_db,
|
||||
account_ids=valid_ids,
|
||||
created_by=current.id,
|
||||
creator_role=current.role,
|
||||
max_geetest_retries=req.max_geetest_retries,
|
||||
proxy_config=proxy,
|
||||
)
|
||||
|
||||
batch_id = runner.batch_id
|
||||
|
||||
# 启动线程
|
||||
thread = threading.Thread(target=runner.run, daemon=True)
|
||||
thread.start()
|
||||
|
||||
return {"batch_id": batch_id, "count": len(valid_ids), "success": True}
|
||||
|
||||
|
||||
@router.get("/tasks", response_model=list[LoginTaskOut])
|
||||
def list_tasks(
|
||||
batch_id: str | None = None,
|
||||
db: Session = Depends(get_db),
|
||||
current: User = Depends(get_current_user),
|
||||
):
|
||||
"""查看登录任务列表。"""
|
||||
query = db.query(LoginTask)
|
||||
|
||||
# 客服只能看自己账号的任务
|
||||
if not has_permission(current.role, "login:view_all"):
|
||||
query = query.join(Account, LoginTask.account_id == Account.id).filter(
|
||||
Account.assigned_to == current.id
|
||||
)
|
||||
|
||||
if batch_id:
|
||||
query = query.filter(LoginTask.batch_id == batch_id)
|
||||
|
||||
tasks = query.order_by(LoginTask.id.desc()).limit(200).all()
|
||||
result = []
|
||||
for t in tasks:
|
||||
acc = db.query(Account).filter(Account.id == t.account_id).first()
|
||||
result.append(LoginTaskOut(
|
||||
id=t.id, batch_id=t.batch_id, account_id=t.account_id,
|
||||
account_username=acc.username if acc else "",
|
||||
status=t.status, cookie=t.cookie or "", message=t.message or "",
|
||||
created_by=t.created_by, created_at=t.created_at, finished_at=t.finished_at,
|
||||
))
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/stop/{batch_id}")
|
||||
def stop_batch(
|
||||
batch_id: str,
|
||||
current: User = Depends(require_permission("login:batch")),
|
||||
):
|
||||
batch = _active_batches.get(batch_id)
|
||||
if batch:
|
||||
batch["runner"].stop()
|
||||
return {"message": "已发送停止信号", "success": True}
|
||||
raise HTTPException(status_code=404, detail="批次不存在或已结束")
|
||||
|
||||
|
||||
@router.websocket("/ws/login/{batch_id}")
|
||||
async def ws_login_logs(websocket: WebSocket, batch_id: str):
|
||||
"""WebSocket 推送登录实时日志。"""
|
||||
await websocket.accept()
|
||||
|
||||
log_queue = asyncio.Queue()
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
# 查找已运行的批次,或等待新批次
|
||||
# 简化:直接把 log_queue 注册到全局,前端创建批次后连 ws
|
||||
_active_batches[batch_id] = {
|
||||
"log_queue": log_queue,
|
||||
"loop": loop,
|
||||
}
|
||||
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
msg = await asyncio.wait_for(log_queue.get(), timeout=30)
|
||||
await websocket.send_json(msg)
|
||||
except asyncio.TimeoutError:
|
||||
await websocket.send_json({"level": "heartbeat", "message": ""})
|
||||
except WebSocketDisconnect:
|
||||
pass
|
||||
finally:
|
||||
_active_batches.pop(batch_id, None)
|
||||
@@ -0,0 +1,146 @@
|
||||
"""代理 & 白名单配置路由"""
|
||||
|
||||
import re
|
||||
import time
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ..database import get_db
|
||||
from ..models import User, ProxyConfig as ProxyConfigModel, AuditLog
|
||||
from ..schemas import ProxyConfigOut, ProxyConfigUpdate, MessageResponse
|
||||
from ..deps import require_permission
|
||||
|
||||
router = APIRouter(prefix="/api/proxy", tags=["代理与白名单"])
|
||||
|
||||
|
||||
def _get_or_create(db: Session) -> ProxyConfigModel:
|
||||
cfg = db.query(ProxyConfigModel).first()
|
||||
if not cfg:
|
||||
cfg = ProxyConfigModel()
|
||||
db.add(cfg)
|
||||
db.commit()
|
||||
db.refresh(cfg)
|
||||
return cfg
|
||||
|
||||
|
||||
@router.get("", response_model=ProxyConfigOut)
|
||||
def get_proxy_config(
|
||||
db: Session = Depends(get_db),
|
||||
_: User = Depends(require_permission("proxy:manage")),
|
||||
):
|
||||
return _get_or_create(db)
|
||||
|
||||
|
||||
@router.put("", response_model=ProxyConfigOut)
|
||||
def update_proxy_config(
|
||||
req: ProxyConfigUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current: User = Depends(require_permission("proxy:manage")),
|
||||
):
|
||||
cfg = _get_or_create(db)
|
||||
cfg.enabled = req.enabled
|
||||
cfg.api_url = req.api_url
|
||||
cfg.http = req.http
|
||||
cfg.https = req.https
|
||||
cfg.whitelist_enabled = req.whitelist_enabled
|
||||
cfg.whitelist_uid = req.whitelist_uid
|
||||
cfg.whitelist_ukey = req.whitelist_ukey
|
||||
db.commit()
|
||||
db.refresh(cfg)
|
||||
|
||||
db.add(AuditLog(user_id=current.id, username=current.username,
|
||||
action="proxy:update", target="proxy_config"))
|
||||
db.commit()
|
||||
return cfg
|
||||
|
||||
|
||||
@router.post("/test")
|
||||
def test_proxy(
|
||||
db: Session = Depends(get_db),
|
||||
current: User = Depends(require_permission("proxy:manage")),
|
||||
):
|
||||
"""测试代理连通性。"""
|
||||
import requests as req_lib
|
||||
cfg = _get_or_create(db)
|
||||
if not cfg.enabled:
|
||||
return {"success": False, "message": "代理未启用"}
|
||||
|
||||
proxy_url = cfg.http or cfg.https
|
||||
if cfg.api_url and not proxy_url:
|
||||
try:
|
||||
resp = req_lib.get(cfg.api_url, timeout=10)
|
||||
match = re.search(r'(\d+\.\d+\.\d+\.\d+):(\d+)', resp.text)
|
||||
if match:
|
||||
proxy_url = f"http://{match.group(1)}:{match.group(2)}"
|
||||
except Exception as e:
|
||||
return {"success": False, "message": f"代理API请求失败: {e}"}
|
||||
|
||||
if not proxy_url:
|
||||
return {"success": False, "message": "无可用代理地址"}
|
||||
|
||||
try:
|
||||
resp = req_lib.get(
|
||||
"https://qifu-api.baidubce.com/ip/local/geo/v1/district",
|
||||
proxies={"http": proxy_url, "https": proxy_url},
|
||||
timeout=(4, 6),
|
||||
headers={"User-Agent": "Mozilla/5.0"},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return {"success": True, "message": f"代理可用,响应: {resp.text[:100]}"}
|
||||
except Exception as e:
|
||||
return {"success": False, "message": f"代理验证失败: {e}"}
|
||||
|
||||
|
||||
@router.post("/whitelist/test")
|
||||
def test_whitelist(
|
||||
db: Session = Depends(get_db),
|
||||
current: User = Depends(require_permission("whitelist:test")),
|
||||
):
|
||||
"""测试白名单连接并自动同步出口IP。"""
|
||||
from douyu.whitelist import WhitelistManager, get_exit_ip_via_proxy
|
||||
import requests as req_lib
|
||||
|
||||
cfg = _get_or_create(db)
|
||||
if not cfg.whitelist_enabled:
|
||||
return {"success": False, "message": "白名单未启用"}
|
||||
if not cfg.whitelist_uid or not cfg.whitelist_ukey:
|
||||
return {"success": False, "message": "未配置白名单UID/UKEY"}
|
||||
|
||||
manager = WhitelistManager(cfg.whitelist_uid, cfg.whitelist_ukey)
|
||||
|
||||
# 测试API连接
|
||||
ok, msg = manager.test_connection()
|
||||
if not ok:
|
||||
return {"success": False, "message": msg}
|
||||
|
||||
# 获取出口IP
|
||||
proxy_url = cfg.http or cfg.https
|
||||
if cfg.api_url and not proxy_url:
|
||||
try:
|
||||
resp = req_lib.get(cfg.api_url, timeout=10)
|
||||
match = re.search(r'(\d+\.\d+\.\d+\.\d+):(\d+)', resp.text)
|
||||
if match:
|
||||
proxy_url = f"http://{match.group(1)}:{match.group(2)}"
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
exit_ip = None
|
||||
if proxy_url:
|
||||
exit_ip = get_exit_ip_via_proxy(proxy_url)
|
||||
if not exit_ip:
|
||||
try:
|
||||
resp = req_lib.get("https://4.ipw.cn", timeout=6, headers={"User-Agent": "Mozilla/5.0"})
|
||||
match = re.search(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', resp.text)
|
||||
if match:
|
||||
exit_ip = match.group(1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not exit_ip:
|
||||
return {"success": False, "message": "API连接正常但无法获取出口IP"}
|
||||
|
||||
# 同步白名单
|
||||
sync_ok, sync_msg = manager.sync_ip(exit_ip)
|
||||
if sync_ok:
|
||||
return {"success": True, "message": f"出口IP {exit_ip} 已同步: {sync_msg}"}
|
||||
return {"success": False, "message": f"同步失败: {sync_msg}"}
|
||||
@@ -0,0 +1,117 @@
|
||||
"""用户管理路由(超管)"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ..database import get_db
|
||||
from ..models import User, AuditLog
|
||||
from ..security import hash_password
|
||||
from ..permissions import ROLE_LABELS, get_role_permissions
|
||||
from ..schemas import UserCreate, UserUpdate, UserInfo
|
||||
from ..deps import require_permission, get_current_user
|
||||
|
||||
router = APIRouter(prefix="/api/users", tags=["用户管理"])
|
||||
|
||||
|
||||
@router.get("", response_model=list[UserInfo])
|
||||
def list_users(
|
||||
db: Session = Depends(get_db),
|
||||
_: User = Depends(require_permission("user:view")),
|
||||
):
|
||||
users = db.query(User).order_by(User.id).all()
|
||||
result = []
|
||||
for u in users:
|
||||
result.append(UserInfo(
|
||||
id=u.id,
|
||||
username=u.username,
|
||||
role=u.role,
|
||||
is_active=u.is_active,
|
||||
remark=u.remark or "",
|
||||
created_at=u.created_at,
|
||||
permissions=get_role_permissions(u.role),
|
||||
))
|
||||
return result
|
||||
|
||||
|
||||
@router.post("", response_model=UserInfo)
|
||||
def create_user(
|
||||
req: UserCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current: User = Depends(require_permission("user:create")),
|
||||
):
|
||||
if db.query(User).filter(User.username == req.username).first():
|
||||
raise HTTPException(status_code=400, detail="用户名已存在")
|
||||
|
||||
user = User(
|
||||
username=req.username,
|
||||
password_hash=hash_password(req.password),
|
||||
role=req.role,
|
||||
remark=req.remark,
|
||||
is_active=True,
|
||||
)
|
||||
db.add(user)
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
|
||||
db.add(AuditLog(user_id=current.id, username=current.username,
|
||||
action="user:create", target=user.username))
|
||||
db.commit()
|
||||
|
||||
return UserInfo(
|
||||
id=user.id, username=user.username, role=user.role,
|
||||
is_active=user.is_active, remark=user.remark or "",
|
||||
permissions=get_role_permissions(user.role),
|
||||
)
|
||||
|
||||
|
||||
@router.put("/{user_id}", response_model=UserInfo)
|
||||
def update_user(
|
||||
user_id: int,
|
||||
req: UserUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current: User = Depends(require_permission("user:edit")),
|
||||
):
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="用户不存在")
|
||||
|
||||
if req.password:
|
||||
user.password_hash = hash_password(req.password)
|
||||
if req.role is not None:
|
||||
user.role = req.role
|
||||
if req.is_active is not None:
|
||||
user.is_active = req.is_active
|
||||
if req.remark is not None:
|
||||
user.remark = req.remark
|
||||
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
|
||||
db.add(AuditLog(user_id=current.id, username=current.username,
|
||||
action="user:edit", target=user.username))
|
||||
db.commit()
|
||||
|
||||
return UserInfo(
|
||||
id=user.id, username=user.username, role=user.role,
|
||||
is_active=user.is_active, remark=user.remark or "",
|
||||
permissions=get_role_permissions(user.role),
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/{user_id}")
|
||||
def delete_user(
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current: User = Depends(require_permission("user:delete")),
|
||||
):
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="用户不存在")
|
||||
if user.role == "super_admin":
|
||||
raise HTTPException(status_code=400, detail="不能删除超级管理员")
|
||||
|
||||
db.add(AuditLog(user_id=current.id, username=current.username,
|
||||
action="user:delete", target=user.username))
|
||||
db.delete(user)
|
||||
db.commit()
|
||||
return {"message": "已删除", "success": True}
|
||||
Reference in New Issue
Block a user