Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 0 additions & 76 deletions .github/workflows/build-webui.yml

This file was deleted.

6 changes: 3 additions & 3 deletions DEPLOY.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ WebUI 开发服务器会启动在 `http://localhost:5173`,并自动代理 API
WebUI 构建产物位于 `static/admin/` 目录。

**自动构建(推荐)**:
- 当 `webui/` 目录下的文件变更并推送到 `main` 分支时,GitHub Actions 会自动构建并提交产物
- PR 合并时会自动触发构建
- 当前由 Vercel 在部署时执行 WebUI 构建(见 `vercel.json` 的 `buildCommand`)
- GitHub Actions 的 WebUI 自动构建流程已关闭

**手动构建**:
```bash
Expand All @@ -145,7 +145,7 @@ npm install
npm run build
```

> **贡献者注意**:修改 WebUI 后无需手动构建,CI 会自动处理
> **贡献者注意**:修改 WebUI 后无需手动构建,Vercel 部署会自动构建

---

Expand Down
141 changes: 1 addition & 140 deletions routes/admin/accounts.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
"""Admin 账号管理模块 - 账号验证和测试"""
"""Admin 账号管理模块 - 账号测试与导入"""
import asyncio
import json
import base64
Expand All @@ -24,145 +24,6 @@
router = APIRouter()


# ----------------------------------------------------------------------
# 账号验证
# ----------------------------------------------------------------------
async def validate_single_account(account: dict) -> dict:
"""验证单个账号的有效性"""
acc_id = get_account_identifier(account)
result = {
"account": acc_id,
"valid": False,
"has_token": bool(account.get("token", "").strip()),
"message": "",
}

def _is_token_invalid(status_code: int, data: dict) -> bool:
msg = (data.get("msg") or data.get("message") or "").lower()
code = data.get("code")
return status_code in {401, 403} or code in {40001, 40002, 40003} or "token" in msg or "unauthorized" in msg

def _create_session(token: str) -> dict:
headers = {**BASE_HEADERS, "authorization": f"Bearer {token}"}
try:
session_resp = cffi_requests.post(
DEEPSEEK_CREATE_SESSION_URL,
headers=headers,
json={"agent": "chat"},
impersonate="safari15_3",
timeout=15,
)
except Exception as e:
return {"success": False, "message": f"请求异常: {e}", "status_code": 0, "data": {}}

try:
data = session_resp.json()
except Exception:
data = {}
finally:
session_resp.close()
if session_resp.status_code == 200 and data.get("code") == 0:
return {
"success": True,
"session_id": data.get("data", {}).get("biz_data", {}).get("id"),
"status_code": session_resp.status_code,
"data": data,
}
return {
"success": False,
"message": data.get("msg") or f"HTTP {session_resp.status_code}",
"status_code": session_resp.status_code,
"data": data,
}

try:
token = account.get("token", "").strip()
if token:
session_result = _create_session(token)
if session_result["success"]:
result["valid"] = True
result["message"] = "Token 有效"
return result

if _is_token_invalid(session_result["status_code"], session_result["data"]):
token = ""
account["token"] = ""

if not token:
try:
login_deepseek_via_account(account)
token = account.get("token", "").strip()
session_result = _create_session(token)
if session_result["success"]:
result["valid"] = True
result["has_token"] = True
result["message"] = "登录成功并验证通过"
else:
result["message"] = f"登录成功但验证失败: {session_result['message']}"
except Exception as e:
result["valid"] = False
result["message"] = f"登录失败: {str(e)}"
except Exception as e:
result["message"] = f"验证出错: {str(e)}"

return result


@router.post("/accounts/validate")
async def validate_account(request: Request, _: bool = Depends(verify_admin)):
"""验证单个账号"""
data = await request.json()
identifier = data.get("identifier", "").strip()

if not identifier:
raise HTTPException(status_code=400, detail="需要账号标识(email 或 mobile)")

account = None
for acc in CONFIG.get("accounts", []):
if acc.get("email") == identifier or acc.get("mobile") == identifier:
account = acc
break

if not account:
raise HTTPException(status_code=404, detail="账号不存在")

result = await validate_single_account(account)

if result["valid"] and result["has_token"]:
save_config(CONFIG)

return JSONResponse(content=result)


@router.post("/accounts/validate-all")
async def validate_all_accounts(_: bool = Depends(verify_admin)):
"""批量验证所有账号"""
accounts = CONFIG.get("accounts", [])
if not accounts:
return JSONResponse(content={
"total": 0, "valid": 0, "invalid": 0, "results": [],
})

results = []
valid_count = 0

for acc in accounts:
result = await validate_single_account(acc)
results.append(result)
if result["valid"]:
valid_count += 1
await asyncio.sleep(0.5)

save_config(CONFIG)

return JSONResponse(content={
"total": len(accounts),
"valid": valid_count,
"invalid": len(accounts) - valid_count,
"results": results,
})


# ----------------------------------------------------------------------
# 账号 API 测试
# ----------------------------------------------------------------------
Expand Down
11 changes: 8 additions & 3 deletions routes/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,14 +290,19 @@ async def webui(request: Request, path: str = ""):
if path and "." in path:
file_path = os.path.join(STATIC_ADMIN_DIR, path)
if os.path.isfile(file_path):
return FileResponse(file_path)
cache_control = "public, max-age=31536000, immutable"
if path.startswith("assets/"):
headers = {"Cache-Control": cache_control}
else:
headers = {"Cache-Control": "no-store, must-revalidate"}
return FileResponse(file_path, headers=headers)
return HTMLResponse(content="Not Found", status_code=404)

# 否则返回 index.html(SPA 路由)
index_path = os.path.join(STATIC_ADMIN_DIR, "index.html")
if os.path.isfile(index_path):
return FileResponse(index_path)
headers = {"Cache-Control": "no-store, must-revalidate"}
return FileResponse(index_path, headers=headers)

return HTMLResponse(content="index.html not found", status_code=404)


29 changes: 25 additions & 4 deletions vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,31 @@
"use": "@vercel/python"
}
],
"routes": [
"buildCommand": "bash scripts/build-webui.sh",
"rewrites": [
{
"src": "/(.*)",
"dest": "app.py"
"source": "/(.*)",
"destination": "/app.py"
}
],
"headers": [
{
"source": "/admin/assets/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
},
{
"source": "/admin/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "no-store, must-revalidate"
}
]
}
]
}
}
Loading