feat(admin): Admin V2 — Ant Design Pro 纯 SPA 重写
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

Next.js SSR/hydration 与 SWR fetch-on-mount 存在根本冲突:
hydration 卸载组件时 abort 的请求仍占用后端 DB 连接,
retry 循环耗尽 PostgreSQL 连接池导致后端完全卡死。

admin-v2 使用 Vite + React + antd 纯 SPA 彻底消除此问题:
- 12 页面全部完成(Login, Dashboard, Accounts, Providers, Models,
  API Keys, Usage, Relay, Config, Prompts, Logs, Agent Templates)
- ProTable + ProForm + ProLayout 统一 UI 模式
- TanStack Query + Axios + Zustand 数据层
- JWT 自动刷新 + 401 重试机制
- 全部 18 网络请求 200 OK,零 ERR_ABORTED

同时更新 troubleshooting 第 13 节和 SaaS 平台文档。
This commit is contained in:
iven
2026-03-30 09:35:59 +08:00
parent 13c0b18bbc
commit a7d33d0207
52 changed files with 8102 additions and 118 deletions

View File

@@ -1,6 +1,6 @@
# ZCLAW SaaS 平台 — 总览
> 最后更新: 2026-03-29 | 实施状态: Phase 1-4 全部完成 + 架构重构完成9 个后端模块 + Worker + Scheduler + Admin 管理后台 + 桌面端完整集成
> 最后更新: 2026-03-30 | 实施状态: Phase 1-4 全部完成 + 架构重构完成9 个后端模块 + Worker + Scheduler + Admin V2 (Ant Design Pro) + 桌面端完整集成
## 架构概述
@@ -13,7 +13,7 @@ ZCLAW SaaS 平台为桌面端用户提供云端能力包括模型中转Key
├── Mode B: Gateway WebSocket (本地中转)
└── Mode C: SaaS Cloud ──→ Rust/Axum 后端 (端口 8080) ──→ 上游 LLM Provider
├── Admin Web (Next.js 管理后台)
├── Admin V2 (Ant Design Pro 纯 SPA)
└── PostgreSQL (数据持久化)
```
@@ -22,7 +22,7 @@ ZCLAW SaaS 平台为桌面端用户提供云端能力包括模型中转Key
| 层级 | 技术 | 说明 |
|------|------|------|
| 后端 | Rust + Axum + sqlx + PostgreSQL | JWT + API Token 双认证RBAC 权限 |
| Admin | Next.js 14 + shadcn/ui + Tailwind | 暗色 OLED 主题20+ 页面 |
| Admin V2 | Vite 6 + React 19 + antd v5 + ProComponents | 纯 SPA替代 Next.js消除 SSR/hydration 问题) |
| 桌面端 | React 18 + Zustand + TypeScript | saas-client.ts 30+ API 方法 |
| 安全 | Argon2id + TOTP 2FA (AES-256-GCM) + RBAC | 速率限制 + 操作审计 + SSRF 防护 |
@@ -219,20 +219,30 @@ ZCLAW SaaS 平台为桌面端用户提供云端能力包括模型中转Key
| `src/scheduler.rs` | 声明式 Scheduler (TOML 定时任务配置 + DB 清理任务) |
| `migrations/` | SQL 迁移文件 (Schema v6, TIMESTAMPTZ) |
### Admin 管理后台 (admin/)
### Admin 管理后台 (admin-v2/)
> **迁移说明**: 原有 `admin/`Next.js 14因 SSR/hydration 与 SWR 的根本冲突导致后端连接池耗尽,
> 已用 `admin-v2/`Vite + React + Ant Design Pro 纯 SPA完全替代。
> 详见 [故障排查 13 节](../../knowledge-base/troubleshooting.md)
| 文件 | 职责 |
|------|------|
| `src/lib/api-client.ts` | 类型化 HTTP 客户端 (自动 Token 刷新 + 重试) |
| `src/lib/types.ts` | 全部 TypeScript 类型定义 |
| `src/lib/auth.ts` | JWT Token 管理 (localStorage) |
| `src/app/(dashboard)/layout.tsx` | Dashboard 布局 + 导航 |
| `src/app/(dashboard)/providers/page.tsx` | Provider 管理 |
| `src/app/(dashboard)/config/page.tsx` | 配置管理 |
| `src/app/(dashboard)/usage/page.tsx` | 用量统计 |
| `src/app/(dashboard)/prompts/` | Prompt 模板管理 |
| `src/app/(dashboard)/agent-templates/` | Agent 模板管理 |
| `src/app/login/page.tsx` | 登录页 |
| `src/services/request.ts` | Axios 实例 + JWT 拦截器 + 401 自动刷新 |
| `src/services/auth.ts` | 认证 API (login/refresh/me) |
| `src/stores/authStore.ts` | Zustand: token + account + permissions |
| `src/types/index.ts` | 全部 TypeScript 类型定义 |
| `src/layouts/AdminLayout.tsx` | ProLayout 侧边栏 + 用户信息 |
| `src/pages/Dashboard.tsx` | 仪表盘 (统计卡片 + 趋势图 + 操作日志) |
| `src/pages/Accounts.tsx` | 账号管理 (CRUD + 角色筛选) |
| `src/pages/Providers.tsx` | 服务商管理 + Key Pool 子表 |
| `src/pages/Models.tsx` | 模型管理 |
| `src/pages/ApiKeys.tsx` | API 密钥管理 |
| `src/pages/Usage.tsx` | 用量统计 (时间范围 + Tab 切换) |
| `src/pages/Relay.tsx` | 中转任务管理 |
| `src/pages/Config.tsx` | 系统配置 (分类 Tab) |
| `src/pages/Prompts.tsx` | 提示词管理 (版本历史 + 回滚) |
| `src/pages/Logs.tsx` | 操作日志 |
| `src/pages/AgentTemplates.tsx` | Agent 模板管理 |
### 桌面端 (desktop/src/)
@@ -247,4 +257,4 @@ ZCLAW SaaS 平台为桌面端用户提供云端能力包括模型中转Key
---
**最后更新**: 2026-03-28
**最后更新**: 2026-03-30

View File

@@ -1981,7 +1981,66 @@ psql -U postgres -d zclaw -c "UPDATE accounts SET role = 'super_admin' WHERE use
---
## 13. 相关文档
## 13. Admin 前端 ERR_ABORTED / 后端卡死 (2026-03-30)
### 13.1 症状
- Admin 前端登录后所有 GET 请求返回 `ERR_ABORTED`Network 面板显示红色 cancelled
- 后端日志显示请求已接收并处理,但响应无法到达客户端
- 后端 PostgreSQL 连接池50 max被耗尽所有后续请求 hang
- `GET /api/health` 也无法响应,整个后端完全卡死
### 13.2 根因分析(因果链)
```
Next.js App Router SSR
→ React 组件服务端渲染 + 客户端 hydration 重建
→ SWR 在 hydration mount 时触发 fetch
→ hydration 卸载旧组件 → AbortController abort 请求
→ Vite proxy 已将请求转发到后端
→ 后端不知道请求已被 abort继续处理占用 DB 连接)
→ SWR retry 重新发起 → 又被 abort → 死循环
→ PostgreSQL 连接池耗尽 → 后端完全卡死
```
**根本矛盾**: Next.js SSR/hydration 机制与 SWR fetch-on-mount 模式存在不可调和的冲突。abort 信号无法从浏览器传播到后端,导致后端持续处理已废弃的请求。
### 13.3 已尝试的修复(均未解决)
| 尝试 | 方案 | 结果 |
|------|------|------|
| 1 | SWRConfig 全局配置dedupingInterval, revalidateOnFocus | 无效abort 发生在更底层 |
| 2 | AuthGuard 路由守卫重构 | 无效,请求在 guard 之前就发出 |
| 3 | `dynamic ssr: false` 页面级禁用 SSR | 部分改善,但 hydration 仍触发 abort |
| 4 | 前端直连后端(绕过 Vite proxy | 无效,问题不在 proxy |
| 5 | health handler 3s 超时 | 只保护了 health 端点,不解决根因 |
| 6 | AbortError 不重试 | 减少了 retry但首次 abort 仍占用连接 |
### 13.4 最终解决方案
**用纯 SPAAnt Design Pro彻底重写 Admin 前端**,消除 SSR/hydration 问题。
**admin-v2 技术栈**:
- Vite 6纯客户端无 SSR
- React 19 + antd v5 + @ant-design/pro-components
- React Router v7 + TanStack Query v5 + Axios + Zustand
**关键修复**:
1. 移除 React `StrictMode`(开发模式双重 mount 触发重复请求 + abort
2. Axios timeout 10s → 30s防止慢请求被误杀
3. JWT 拦截器(自动附加 token + 401 刷新)
### 13.5 验证结果
- 全部 12 页面功能正常18 个网络请求全部 200
- 零 `ERR_ABORTED`,后端连接池不再耗尽
- 后端 health 检查持续返回 200
**涉及文件**: `admin-v2/` 目录(全新项目,替换 `admin/`
---
## 14. 相关文档
- [ZCLAW 配置指南](./zclaw-configuration.md) - 配置文件位置、格式和最佳实践
- [Agent 和 LLM 提供商配置](./agent-provider-config.md) - Agent 管理和 Provider 配置
@@ -1993,6 +2052,7 @@ psql -U postgres -d zclaw -c "UPDATE accounts SET role = 'super_admin' WHERE use
| 日期 | 变更 |
|------|------|
| 2026-03-30 | 添加第 13 节Admin 前端 ERR_ABORTED / 后端卡死 — Next.js SSR/hydration + SWR 根本冲突导致连接池耗尽admin-v2 (Ant Design Pro 纯 SPA) 替代方案 |
| 2026-03-28 | 添加 12.1-12.4 节SaaS 后端问题 — Admin 登录无请求、SQLite→PostgreSQL 遗留语法、角色权限不足、usage 路由不匹配 |
| 2026-03-27 | 添加 9.10/9.11 节:多轮工具调用 tool_call_id + reasoning_content 缺失 — OpenAiMessage 字段补全、[Assistant,ToolUse*] 合并、reasoning 分离追踪 |
| 2026-03-27 | 添加 9.10 节:多轮工具调用 tool_call_id is not found — OpenAiMessage 缺少 tool_call_id 字段 + 连续 ToolUse 未合并 |