Files
ds2api/routes/admin/config.py

184 lines
6.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""Admin 配置管理模块 - 配置、API Keys、账号管理"""
import os
from fastapi import APIRouter, HTTPException, Request, Depends
from fastapi.responses import JSONResponse
from core.config import CONFIG, save_config, logger
from core.auth import init_account_queue, get_queue_status, get_account_identifier
from core.deepseek import login_deepseek_via_account
from .auth import verify_admin
router = APIRouter()
# Vercel 预配置
VERCEL_TOKEN = os.getenv("VERCEL_TOKEN", "")
VERCEL_PROJECT_ID = os.getenv("VERCEL_PROJECT_ID", "")
VERCEL_TEAM_ID = os.getenv("VERCEL_TEAM_ID", "")
# ----------------------------------------------------------------------
# Vercel 预配置信息
# ----------------------------------------------------------------------
@router.get("/vercel/config")
async def get_vercel_config(_: bool = Depends(verify_admin)):
"""获取预配置的 Vercel 信息(脱敏)"""
return JSONResponse(content={
"has_token": bool(VERCEL_TOKEN),
"project_id": VERCEL_PROJECT_ID,
"team_id": VERCEL_TEAM_ID or None,
})
# ----------------------------------------------------------------------
# 配置管理
# ----------------------------------------------------------------------
@router.get("/config")
async def get_config(_: bool = Depends(verify_admin)):
"""获取当前配置(密码脱敏)"""
safe_config = {
"keys": CONFIG.get("keys", []),
"accounts": [],
"claude_mapping": CONFIG.get("claude_mapping", {}),
}
for acc in CONFIG.get("accounts", []):
safe_acc = {
"email": acc.get("email", ""),
"mobile": acc.get("mobile", ""),
"has_password": bool(acc.get("password")),
"has_token": bool(acc.get("token")),
"token_preview": acc.get("token", "")[:20] + "..." if acc.get("token") else "",
}
safe_config["accounts"].append(safe_acc)
return JSONResponse(content=safe_config)
@router.post("/config")
async def update_config(request: Request, _: bool = Depends(verify_admin)):
"""更新完整配置"""
data = await request.json()
if "keys" in data:
CONFIG["keys"] = data["keys"]
if "accounts" in data:
# 保留原有密码和 token
existing = {get_account_identifier(a): a for a in CONFIG.get("accounts", [])}
for acc in data["accounts"]:
acc_id = get_account_identifier(acc)
if acc_id in existing:
if not acc.get("password"):
acc["password"] = existing[acc_id].get("password", "")
if not acc.get("token"):
acc["token"] = existing[acc_id].get("token", "")
CONFIG["accounts"] = data["accounts"]
init_account_queue()
if "claude_mapping" in data:
CONFIG["claude_mapping"] = data["claude_mapping"]
save_config(CONFIG)
return JSONResponse(content={"success": True, "message": "配置已更新"})
# ----------------------------------------------------------------------
# API Keys 管理
# ----------------------------------------------------------------------
@router.post("/keys")
async def add_key(request: Request, _: bool = Depends(verify_admin)):
"""添加 API Key"""
data = await request.json()
key = data.get("key", "").strip()
if not key:
raise HTTPException(status_code=400, detail="Key 不能为空")
if key in CONFIG.get("keys", []):
raise HTTPException(status_code=400, detail="Key 已存在")
if "keys" not in CONFIG:
CONFIG["keys"] = []
CONFIG["keys"].append(key)
save_config(CONFIG)
return JSONResponse(content={"success": True, "total_keys": len(CONFIG["keys"])})
@router.delete("/keys/{key}")
async def delete_key(key: str, _: bool = Depends(verify_admin)):
"""删除 API Key"""
if key not in CONFIG.get("keys", []):
raise HTTPException(status_code=404, detail="Key 不存在")
CONFIG["keys"].remove(key)
save_config(CONFIG)
return JSONResponse(content={"success": True, "total_keys": len(CONFIG["keys"])})
# ----------------------------------------------------------------------
# 账号管理
# ----------------------------------------------------------------------
@router.post("/accounts")
async def add_account(request: Request, _: bool = Depends(verify_admin)):
"""添加账号"""
data = await request.json()
email = data.get("email", "").strip()
mobile = data.get("mobile", "").strip()
password = data.get("password", "").strip()
token = data.get("token", "").strip()
if not email and not mobile:
raise HTTPException(status_code=400, detail="需要 email 或 mobile")
# 检查是否已存在
for acc in CONFIG.get("accounts", []):
if email and acc.get("email") == email:
raise HTTPException(status_code=400, detail="邮箱已存在")
if mobile and acc.get("mobile") == mobile:
raise HTTPException(status_code=400, detail="手机号已存在")
new_account = {}
if email:
new_account["email"] = email
if mobile:
new_account["mobile"] = mobile
if password:
new_account["password"] = password
if token:
new_account["token"] = token
if "accounts" not in CONFIG:
CONFIG["accounts"] = []
CONFIG["accounts"].append(new_account)
init_account_queue()
save_config(CONFIG)
return JSONResponse(content={"success": True, "total_accounts": len(CONFIG["accounts"])})
@router.delete("/accounts/{identifier}")
async def delete_account(identifier: str, _: bool = Depends(verify_admin)):
"""删除账号(通过 email 或 mobile"""
accounts = CONFIG.get("accounts", [])
for i, acc in enumerate(accounts):
if acc.get("email") == identifier or acc.get("mobile") == identifier:
accounts.pop(i)
init_account_queue()
save_config(CONFIG)
return JSONResponse(content={"success": True, "total_accounts": len(accounts)})
raise HTTPException(status_code=404, detail="账号不存在")
# ----------------------------------------------------------------------
# 账号队列状态
# ----------------------------------------------------------------------
@router.get("/queue/status")
async def get_account_queue_status(_: bool = Depends(verify_admin)):
"""获取账号轮询队列状态"""
return JSONResponse(content=get_queue_status())