docs(saas): 添加 SaaS 后台系统设计规格
涵盖四大核心模块的完整设计: - 账号权限管理 (JWT/Argon2/TOTP/权限模板) - 模型与 API 配置中心 (提供商/模型/密钥/使用量) - 模型请求中转服务 (队列/流式转发/速率限制) - 系统配置迁移分析 (迁移优先级/同步协议) 技术栈: Rust + Axum 后端 + React 管理后台 目标: MVP <100 用户, 独立 SaaS 服务
This commit is contained in:
763
docs/superpowers/specs/2026-03-27-saas-backend-design.md
Normal file
763
docs/superpowers/specs/2026-03-27-saas-backend-design.md
Normal file
@@ -0,0 +1,763 @@
|
||||
# ZCLAW SaaS 后台系统设计规格
|
||||
|
||||
> 日期: 2026-03-27
|
||||
> 状态: 待审阅
|
||||
> 范围: 独立 SaaS 后端服务 (Rust + Axum) + 独立 Web 管理后台 (React + TypeScript)
|
||||
|
||||
## 1. 概述
|
||||
|
||||
### 1.1 问题陈述
|
||||
|
||||
ZCLAW 当前是纯桌面单用户应用,缺少用户账号系统、API 服务端、多租户支持和请求中转能力。所有 LLM 调用直接从桌面端发起,API 密钥存储在本地环境变量或 OS Keyring,模型配置散落在 TOML 文件和 localStorage 中。这限制了多用户协作、集中管控和资源优化。
|
||||
|
||||
### 1.2 设计目标
|
||||
|
||||
1. 为 ZCLAW 添加独立 SaaS 后端,提供账号权限、模型配置、请求中转、配置迁移四大能力
|
||||
2. 桌面端通过简单配置切换即可接入 SaaS(改 `base_url` + 添加 API Token)
|
||||
3. MVP 阶段支持 <100 用户,后续可扩展至更大规模
|
||||
|
||||
### 1.3 用户决策
|
||||
|
||||
| 决策点 | 选择 |
|
||||
|--------|------|
|
||||
| 部署形态 | 独立 SaaS 后端服务 |
|
||||
| 技术栈 | Rust + Axum |
|
||||
| 实施顺序 | 按依赖顺序: 账号→API→中转→迁移 |
|
||||
| 管理界面 | 独立 React Web 管理后台 |
|
||||
| 用户规模 | MVP <100 用户 |
|
||||
| 支持模型 | 智谱 GLM, 通义千问, Kimi, DeepSeek |
|
||||
| 代码位置 | 同仓库 workspace 成员 `crates/zclaw-saas/` |
|
||||
|
||||
---
|
||||
|
||||
## 2. 架构
|
||||
|
||||
### 2.1 系统架构图
|
||||
|
||||
```
|
||||
┌─────────────────────┐ ┌─────────────────────────────────────────┐
|
||||
│ ZCLAW 桌面客户端 │ │ SaaS 后端服务 │
|
||||
│ (Tauri + React) │ │ (Rust + Axum) │
|
||||
│ │ │ │
|
||||
│ OpenAiDriver ──────┼────►│ /api/v1/relay/chat/completions │
|
||||
│ (改 base_url) │ │ → 权限检查 → 队列 → 转发 → 流式响应 │
|
||||
│ │ │ │
|
||||
│ SaaSConfigClient ──┼────►│ /api/v1/config/sync │
|
||||
│ (配置同步) │ │ → 配置覆盖/冲突检测 │
|
||||
│ │ │ │
|
||||
│ Model Picker ──────┼────►│ /api/v1/catalog/models │
|
||||
│ (模型目录) │ │ → 动态模型列表 │
|
||||
└─────────────────────┘ └─────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐
|
||||
│ SQLite (WAL) │
|
||||
│ saas-data.db │
|
||||
└───────────────┘
|
||||
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ 管理后台 (React + TypeScript) │
|
||||
│ /admin/accounts /admin/providers /admin/relay │
|
||||
│ /admin/roles /admin/usage /admin/migration │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 Crate 依赖
|
||||
|
||||
```
|
||||
zclaw-types (无依赖,复用 ID/错误/消息类型)
|
||||
↑
|
||||
zclaw-saas (→ types + axum/sqlx/argon2/jsonwebtoken)
|
||||
↑ (通过 API)
|
||||
desktop/ (→ 通过 HTTP API 调用 SaaS)
|
||||
saas-admin/ (→ 通过 HTTP API 调用 SaaS)
|
||||
```
|
||||
|
||||
### 2.3 文件结构
|
||||
|
||||
```
|
||||
crates/zclaw-saas/
|
||||
Cargo.toml
|
||||
src/
|
||||
lib.rs # 模块声明
|
||||
main.rs # 入口
|
||||
error.rs # SaaS 错误类型
|
||||
config.rs # SaaS 服务器配置 (TOML)
|
||||
db.rs # 数据库初始化 + 迁移
|
||||
state.rs # AppState
|
||||
auth/
|
||||
mod.rs
|
||||
jwt.rs # JWT 创建/验证
|
||||
password.rs # Argon2 哈希
|
||||
totp.rs # TOTP 2FA
|
||||
middleware.rs # Axum 认证中间件
|
||||
account/
|
||||
mod.rs
|
||||
types.rs # Account/Role/Permission 类型
|
||||
handlers.rs # HTTP 处理器
|
||||
service.rs # 业务逻辑
|
||||
model_config/
|
||||
mod.rs
|
||||
types.rs # Provider/Model/APIKey 类型
|
||||
handlers.rs # HTTP 处理器
|
||||
service.rs # 业务逻辑
|
||||
relay/
|
||||
mod.rs
|
||||
types.rs # 请求/响应/队列类型
|
||||
handlers.rs # HTTP 处理器 (代理端点)
|
||||
service.rs # 队列管理、调度、批处理
|
||||
provider_impl.rs # 提供商特定转发逻辑
|
||||
migration/
|
||||
mod.rs
|
||||
types.rs # ConfigItem/SyncRecord 类型
|
||||
handlers.rs # HTTP 处理器
|
||||
service.rs # 配置分析和同步逻辑
|
||||
|
||||
saas-admin/ # 独立 React 管理后台
|
||||
package.json
|
||||
src/
|
||||
App.tsx
|
||||
pages/
|
||||
Login.tsx
|
||||
Dashboard.tsx
|
||||
accounts/
|
||||
providers/
|
||||
api-keys/
|
||||
usage/
|
||||
relay/
|
||||
migration/
|
||||
roles/
|
||||
logs/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据库设计
|
||||
|
||||
### 3.1 概述
|
||||
|
||||
- 引擎: SQLite WAL 模式
|
||||
- 文件: 独立于桌面端 `~/.zclaw/data.db`,默认 `./saas-data.db`
|
||||
- 迁移: 版本化 schema,启动时自动迁移
|
||||
|
||||
### 3.2 完整 Schema
|
||||
|
||||
```sql
|
||||
-- Schema 版本控制
|
||||
CREATE TABLE IF NOT EXISTS saas_schema_version (
|
||||
version INTEGER PRIMARY KEY
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 模块一: 账号权限
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS accounts (
|
||||
id TEXT PRIMARY KEY, -- UUID v4
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL, -- Argon2id
|
||||
display_name TEXT NOT NULL DEFAULT '',
|
||||
avatar_url TEXT,
|
||||
role TEXT NOT NULL DEFAULT 'user', -- 'super_admin' | 'admin' | 'user'
|
||||
status TEXT NOT NULL DEFAULT 'active', -- 'active' | 'disabled' | 'suspended'
|
||||
totp_secret TEXT, -- 加密存储
|
||||
totp_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
last_login_at TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_accounts_email ON accounts(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_accounts_role ON accounts(role);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS api_tokens (
|
||||
id TEXT PRIMARY KEY, -- UUID
|
||||
account_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL, -- e.g. "桌面客户端"
|
||||
token_hash TEXT NOT NULL, -- SHA256(token)
|
||||
token_prefix TEXT NOT NULL, -- 前 8 字符用于展示
|
||||
permissions TEXT NOT NULL DEFAULT '[]', -- JSON 权限数组
|
||||
last_used_at TEXT,
|
||||
expires_at TEXT, -- NULL = 永不过期
|
||||
created_at TEXT NOT NULL,
|
||||
revoked_at TEXT,
|
||||
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_api_tokens_account ON api_tokens(account_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_api_tokens_hash ON api_tokens(token_hash);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS roles (
|
||||
id TEXT PRIMARY KEY, -- 'super_admin' | 'admin' | 'user' | 自定义 UUID
|
||||
name TEXT NOT NULL, -- 显示名称 (中文)
|
||||
description TEXT,
|
||||
permissions TEXT NOT NULL DEFAULT '[]', -- JSON 权限数组
|
||||
is_system INTEGER NOT NULL DEFAULT 0, -- 系统角色不可删除
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS permission_templates (
|
||||
id TEXT PRIMARY KEY, -- UUID
|
||||
name TEXT NOT NULL, -- e.g. "标准用户", "只读用户"
|
||||
description TEXT,
|
||||
permissions TEXT NOT NULL DEFAULT '[]', -- JSON 权限数组
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS operation_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
account_id TEXT, -- NULL = 系统操作
|
||||
action TEXT NOT NULL, -- e.g. "account.create", "model.update"
|
||||
target_type TEXT, -- e.g. "account", "api_key", "model"
|
||||
target_id TEXT,
|
||||
details TEXT, -- JSON 详情
|
||||
ip_address TEXT,
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_op_logs_account ON operation_logs(account_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_op_logs_action ON operation_logs(action);
|
||||
CREATE INDEX IF NOT EXISTS idx_op_logs_time ON operation_logs(created_at);
|
||||
|
||||
-- ============================================================
|
||||
-- 模块二: 模型配置
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS providers (
|
||||
id TEXT PRIMARY KEY, -- UUID
|
||||
name TEXT NOT NULL UNIQUE, -- e.g. 'zhipu', 'qwen', 'kimi', 'deepseek'
|
||||
display_name TEXT NOT NULL, -- e.g. '智谱 AI'
|
||||
api_key TEXT, -- 服务端提供商 API 密钥 (加密存储)
|
||||
base_url TEXT NOT NULL,
|
||||
api_protocol TEXT NOT NULL DEFAULT 'openai', -- 'openai' | 'anthropic'
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
rate_limit_rpm INTEGER, -- 每分钟请求数
|
||||
rate_limit_tpm INTEGER, -- 每分钟 token 数
|
||||
config_json TEXT DEFAULT '{}', -- 提供商特定配置
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS models (
|
||||
id TEXT PRIMARY KEY, -- UUID
|
||||
provider_id TEXT NOT NULL,
|
||||
model_id TEXT NOT NULL, -- 传给 API 的模型标识
|
||||
alias TEXT NOT NULL, -- 显示名称
|
||||
context_window INTEGER NOT NULL DEFAULT 8192,
|
||||
max_output_tokens INTEGER NOT NULL DEFAULT 4096,
|
||||
supports_streaming INTEGER NOT NULL DEFAULT 1,
|
||||
supports_vision INTEGER NOT NULL DEFAULT 0,
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
pricing_input REAL DEFAULT 0, -- 每 1K token 价格
|
||||
pricing_output REAL DEFAULT 0,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
UNIQUE(provider_id, model_id),
|
||||
FOREIGN KEY (provider_id) REFERENCES providers(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_models_provider ON models(provider_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS account_api_keys (
|
||||
id TEXT PRIMARY KEY, -- UUID
|
||||
account_id TEXT NOT NULL,
|
||||
provider_id TEXT NOT NULL,
|
||||
key_value TEXT NOT NULL, -- API 密钥 (加密存储)
|
||||
key_label TEXT, -- e.g. "主密钥", "备用密钥"
|
||||
permissions TEXT NOT NULL DEFAULT '[]', -- JSON: 可访问的模型 ID 列表
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
last_used_at TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
revoked_at TEXT,
|
||||
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (provider_id) REFERENCES providers(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_account_api_keys_account ON account_api_keys(account_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS usage_records (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
account_id TEXT NOT NULL,
|
||||
provider_id TEXT NOT NULL,
|
||||
model_id TEXT NOT NULL,
|
||||
input_tokens INTEGER NOT NULL DEFAULT 0,
|
||||
output_tokens INTEGER NOT NULL DEFAULT 0,
|
||||
latency_ms INTEGER,
|
||||
status TEXT NOT NULL DEFAULT 'success', -- 'success' | 'error' | 'rate_limited'
|
||||
error_message TEXT,
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_usage_account ON usage_records(account_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_usage_time ON usage_records(created_at);
|
||||
|
||||
-- ============================================================
|
||||
-- 模块三: 中转服务
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS relay_tasks (
|
||||
id TEXT PRIMARY KEY, -- UUID
|
||||
account_id TEXT NOT NULL,
|
||||
provider_id TEXT NOT NULL,
|
||||
model_id TEXT NOT NULL,
|
||||
request_hash TEXT NOT NULL, -- 用于去重
|
||||
status TEXT NOT NULL DEFAULT 'queued', -- 'queued' | 'processing' | 'completed' | 'failed'
|
||||
priority INTEGER NOT NULL DEFAULT 0,
|
||||
attempt_count INTEGER NOT NULL DEFAULT 0,
|
||||
max_attempts INTEGER NOT NULL DEFAULT 3,
|
||||
request_body TEXT NOT NULL, -- JSON: 转发的请求体
|
||||
response_body TEXT, -- JSON: 响应体
|
||||
input_tokens INTEGER DEFAULT 0,
|
||||
output_tokens INTEGER DEFAULT 0,
|
||||
error_message TEXT,
|
||||
queued_at TEXT NOT NULL,
|
||||
started_at TEXT,
|
||||
completed_at TEXT,
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_relay_status ON relay_tasks(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_relay_account ON relay_tasks(account_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_relay_provider ON relay_tasks(provider_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 模块四: 配置迁移
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS config_items (
|
||||
id TEXT PRIMARY KEY, -- UUID
|
||||
category TEXT NOT NULL, -- 'llm' | 'agent' | 'security' | 'hands' | 'skills' | 'tools' | 'logging' | 'desktop' | 'server'
|
||||
key_path TEXT NOT NULL, -- e.g. 'llm.default_provider'
|
||||
value_type TEXT NOT NULL, -- 'string' | 'integer' | 'float' | 'boolean' | 'array' | 'object'
|
||||
current_value TEXT, -- JSON 编码的当前值
|
||||
default_value TEXT, -- JSON 编码的默认值
|
||||
source TEXT NOT NULL DEFAULT 'local', -- 'local' | 'saas' | 'override'
|
||||
description TEXT, -- 中文描述
|
||||
requires_restart INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
UNIQUE(category, key_path)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_config_category ON config_items(category);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS config_sync_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
account_id TEXT NOT NULL,
|
||||
client_fingerprint TEXT NOT NULL,
|
||||
action TEXT NOT NULL, -- 'push' | 'pull' | 'conflict'
|
||||
config_keys TEXT NOT NULL, -- JSON: 受影响的配置键
|
||||
client_values TEXT, -- JSON: 客户端值
|
||||
saas_values TEXT, -- JSON: SaaS 值
|
||||
resolution TEXT, -- 'client_wins' | 'saas_wins' | 'manual'
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_sync_account ON config_sync_log(account_id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 模块一: 账号权限管理
|
||||
|
||||
### 4.1 认证机制
|
||||
|
||||
| 机制 | 实现 | 用途 |
|
||||
|------|------|------|
|
||||
| 密码 | Argon2id (argon2 crate) | 账号登录 |
|
||||
| JWT | HMAC-SHA256 (jsonwebtoken crate) | 管理后台会话 |
|
||||
| TOTP 2FA | totp-rs crate | 可选双因素认证 |
|
||||
| API Token | SHA-256 哈希存储 | 桌面客户端/程序化访问 |
|
||||
|
||||
**JWT Payload**: `{ sub: account_id, role: "admin", exp: timestamp }`
|
||||
**API Token 格式**: `zclaw_<48 random bytes>`,仅创建时展示一次
|
||||
|
||||
### 4.2 认证中间件
|
||||
|
||||
```rust
|
||||
// auth/middleware.rs
|
||||
pub struct AuthContext {
|
||||
pub account_id: Uuid,
|
||||
pub role: AccountRole,
|
||||
pub permissions: Vec<String>,
|
||||
pub auth_method: AuthMethod, // Jwt | ApiToken
|
||||
}
|
||||
|
||||
// 中间件从 Authorization: Bearer <jwt> 或 X-API-Key: <token> 提取身份
|
||||
// 将 AuthContext 注入 request extensions
|
||||
```
|
||||
|
||||
### 4.3 角色与权限
|
||||
|
||||
权限使用字符串常量,检查时匹配字符串列表:
|
||||
|
||||
```rust
|
||||
pub mod permissions {
|
||||
pub const ACCOUNT_READ: &str = "account:read";
|
||||
pub const ACCOUNT_WRITE: &str = "account:write";
|
||||
pub const ACCOUNT_ADMIN: &str = "account:admin";
|
||||
pub const MODEL_READ: &str = "model:read";
|
||||
pub const MODEL_WRITE: &str = "model:write";
|
||||
pub const RELAY_USE: &str = "relay:use";
|
||||
pub const RELAY_ADMIN: &str = "relay:admin";
|
||||
pub const CONFIG_READ: &str = "config:read";
|
||||
pub const CONFIG_WRITE: &str = "config:write";
|
||||
pub const ADMIN_FULL: &str = "admin:full";
|
||||
}
|
||||
```
|
||||
|
||||
| 角色 | 权限 |
|
||||
|------|------|
|
||||
| `super_admin` | `admin:full` (授权检查时直接放行) |
|
||||
| `admin` | account:read/write, model:read/write, relay:use/admin, config:read/write |
|
||||
| `user` | model:read, relay:use, config:read |
|
||||
|
||||
### 4.4 API 端点
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| POST | `/api/v1/auth/register` | admin:full | 创建账号 |
|
||||
| POST | `/api/v1/auth/login` | 公开 | 登录返回 JWT |
|
||||
| POST | `/api/v1/auth/refresh` | JWT | 刷新 JWT |
|
||||
| POST | `/api/v1/auth/totp/setup` | JWT | 获取 TOTP 设置 URI |
|
||||
| POST | `/api/v1/auth/totp/verify` | JWT | 验证并启用 TOTP |
|
||||
| POST | `/api/v1/auth/totp/disable` | JWT | 禁用 TOTP |
|
||||
| GET | `/api/v1/accounts` | account:read | 账号列表 |
|
||||
| GET | `/api/v1/accounts/:id` | account:read | 账号详情 |
|
||||
| PUT | `/api/v1/accounts/:id` | account:write | 更新账号 |
|
||||
| PATCH | `/api/v1/accounts/:id/status` | account:admin | 启用/禁用 |
|
||||
| GET | `/api/v1/tokens` | - (本人) | 列出 API 令牌 |
|
||||
| POST | `/api/v1/tokens` | - (本人) | 创建令牌 |
|
||||
| DELETE | `/api/v1/tokens/:id` | - (本人) | 撤销令牌 |
|
||||
| GET | `/api/v1/roles` | account:read | 角色列表 |
|
||||
| POST | `/api/v1/roles` | account:admin | 创建自定义角色 |
|
||||
| PUT | `/api/v1/roles/:id` | account:admin | 更新角色权限 |
|
||||
| DELETE | `/api/v1/roles/:id` | account:admin | 删除自定义角色 |
|
||||
| GET | `/api/v1/permission-templates` | account:read | 模板列表 |
|
||||
| POST | `/api/v1/permission-templates` | account:admin | 创建模板 |
|
||||
| POST | `/api/v1/permission-templates/:id/apply` | account:admin | 批量应用 |
|
||||
| GET | `/api/v1/logs/operations` | account:read | 操作日志 (分页) |
|
||||
|
||||
### 4.5 关键数据结构
|
||||
|
||||
```rust
|
||||
pub struct Account {
|
||||
pub id: Uuid,
|
||||
pub username: String,
|
||||
pub email: String,
|
||||
pub password_hash: String, // skip_serializing
|
||||
pub display_name: String,
|
||||
pub avatar_url: Option<String>,
|
||||
pub role: AccountRole,
|
||||
pub status: AccountStatus,
|
||||
pub totp_enabled: bool,
|
||||
pub last_login_at: Option<DateTime<Utc>>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub enum AccountRole { SuperAdmin, Admin, User }
|
||||
pub enum AccountStatus { Active, Disabled, Suspended }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 模块二: 模型与 API 配置中心
|
||||
|
||||
### 5.1 提供商管理
|
||||
|
||||
提供商存储服务端 API 密钥 (加密) 和基础配置。管理员创建提供商后,可为每个提供商添加多个模型。
|
||||
|
||||
**API 密钥加密**: AES-256-GCM,密钥通过 HKDF-SHA256 从服务器 JWT 密钥派生。
|
||||
|
||||
### 5.2 账号级 API 密钥
|
||||
|
||||
每个账号可以为每个提供商配置独立的 API 密钥。密钥权限 (JSON 数组) 控制该密钥可访问的模型 ID 列表。支持:
|
||||
- 生成: 创建新密钥,返回明文 (仅一次)
|
||||
- 轮换: 生成新密钥替换旧密钥
|
||||
- 禁用: 临时禁用
|
||||
- 撤销: 永久删除
|
||||
|
||||
### 5.3 使用量统计
|
||||
|
||||
每次中转请求完成后记录 `usage_records`。统计查询使用 SQL 聚合,MVP 阶段不需要预计算视图。
|
||||
|
||||
### 5.4 API 端点
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | `/api/v1/providers` | model:read | 提供商列表 |
|
||||
| POST | `/api/v1/providers` | model:write | 创建提供商 |
|
||||
| GET | `/api/v1/providers/:id` | model:read | 提供商详情 |
|
||||
| PUT | `/api/v1/providers/:id` | model:write | 更新提供商 |
|
||||
| PATCH | `/api/v1/providers/:id/status` | model:write | 启用/禁用 |
|
||||
| GET | `/api/v1/providers/:id/models` | model:read | 模型列表 |
|
||||
| POST | `/api/v1/providers/:id/models` | model:write | 添加模型 |
|
||||
| PUT | `/api/v1/models/:id` | model:write | 更新模型 |
|
||||
| PATCH | `/api/v1/models/:id/status` | model:write | 启用/禁用模型 |
|
||||
| GET | `/api/v1/account/api-keys` | - (本人) | 密钥列表 |
|
||||
| POST | `/api/v1/account/api-keys` | - (本人) | 创建密钥 |
|
||||
| PUT | `/api/v1/account/api-keys/:id` | - (本人) | 更新权限/标签 |
|
||||
| POST | `/api/v1/account/api-keys/:id/rotate` | - (本人) | 轮换密钥 |
|
||||
| PATCH | `/api/v1/account/api-keys/:id/status` | - (本人) | 启用/禁用 |
|
||||
| DELETE | `/api/v1/account/api-keys/:id` | - (本人) | 撤销密钥 |
|
||||
| GET | `/api/v1/usage/stats` | relay:admin | 总体统计 |
|
||||
| GET | `/api/v1/usage/daily` | - (本人) | 每日统计 |
|
||||
| GET | `/api/v1/usage/account/:id` | relay:admin | 账号统计 |
|
||||
| GET | `/api/v1/catalog/models` | model:read | 公开模型目录 |
|
||||
|
||||
### 5.5 初始种子数据
|
||||
|
||||
从 `config/chinese-providers.toml` 导入:
|
||||
|
||||
| 提供商 | 模型 |
|
||||
|--------|------|
|
||||
| 智谱 AI | GLM-4-Plus, GLM-4-Flash, GLM-4V-Plus, GLM-Z1-AirX |
|
||||
| 通义千问 | Qwen-Max, Qwen-Plus, Qwen-Turbo, Qwen-VL-Max |
|
||||
| Kimi | moonshot-v1-8k, moonshot-v1-32k, moonshot-v1-128k |
|
||||
| DeepSeek | deepseek-chat, deepseek-reasoner |
|
||||
|
||||
---
|
||||
|
||||
## 6. 模块三: 模型请求中转服务
|
||||
|
||||
### 6.1 请求流
|
||||
|
||||
```
|
||||
桌面客户端
|
||||
POST /api/v1/relay/chat/completions
|
||||
Headers: Authorization: Bearer <api_token> 或 X-API-Key: <token>
|
||||
Body: { model: "glm-4-plus", messages: [...], stream: true }
|
||||
↓
|
||||
认证中间件 → 提取 account_id + permissions
|
||||
↓
|
||||
权限检查 → relay:use
|
||||
↓
|
||||
提供商解析 → models 表查找 provider_id → providers 表获取 base_url + api_protocol
|
||||
↓
|
||||
API 密钥解析 → account_api_keys 表获取该账号的提供商密钥
|
||||
↓
|
||||
速率限制检查 → 令牌桶算法
|
||||
↓
|
||||
队列调度 → tokio::sync::mpsc (如并发已满则排队)
|
||||
↓
|
||||
转发请求 → reqwest::Client POST 到上游提供商
|
||||
↓
|
||||
流式响应 → SSE 转发给客户端 (stream: true)
|
||||
↓
|
||||
记录使用量 → usage_records 表
|
||||
```
|
||||
|
||||
### 6.2 队列与调度
|
||||
|
||||
```rust
|
||||
pub struct RelayService {
|
||||
queues: Arc<DashMap<Uuid, mpsc::Sender<RelayTask>>>, // 每提供商独立队列
|
||||
semaphores: Arc<DashMap<Uuid, Arc<Semaphore>>>, // 并发控制
|
||||
clients: Arc<DashMap<Uuid, reqwest::Client>>, // 每提供商 HTTP 客户端
|
||||
config: RelayConfig,
|
||||
}
|
||||
```
|
||||
|
||||
- 每提供商一个 mpsc channel (容量 100)
|
||||
- 每提供商一个 Semaphore 控制并发 (默认 5)
|
||||
- Worker task 从 channel 拉取请求并执行
|
||||
|
||||
### 6.3 速率限制
|
||||
|
||||
令牌桶算法:
|
||||
- RPM (每分钟请求数): `tokio::sync::Semaphore` + 定时释放
|
||||
- TPM (每分钟 token 数): 基于最近一分钟 `usage_records` 的 SQL 查询
|
||||
|
||||
### 6.4 错误处理与重试
|
||||
|
||||
- 瞬态错误 (超时, 5xx): 指数退避重试 (1s, 2s, 4s),最多 3 次
|
||||
- 客户端错误 (4xx, 认证失败): 立即返回,不重试
|
||||
- 提供商不可用: 标记 provider disabled,通知管理员
|
||||
|
||||
### 6.5 API 端点
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| POST | `/api/v1/relay/chat/completions` | relay:use | 中转聊天补全 (支持流式) |
|
||||
| GET | `/api/v1/relay/status` | relay:admin | 队列状态/提供商健康 |
|
||||
| GET | `/api/v1/relay/tasks` | relay:admin | 任务列表 |
|
||||
| GET | `/api/v1/relay/tasks/:id` | relay:admin | 任务详情 |
|
||||
| DELETE | `/api/v1/relay/tasks/:id` | relay:admin | 取消排队任务 |
|
||||
| POST | `/api/v1/relay/retry/:id` | relay:admin | 重试失败任务 |
|
||||
|
||||
### 6.6 桌面端集成
|
||||
|
||||
桌面端 `OpenAiDriver` 将 `base_url` 指向 SaaS 中转:
|
||||
```
|
||||
原来: base_url = "https://open.bigmodel.cn/api/paas/v4"
|
||||
改为: base_url = "https://saas.zclaw.example.com/api/v1/relay"
|
||||
添加: X-API-Key: zclaw_xxxxx (账号的 API Token)
|
||||
```
|
||||
|
||||
中转保持 OpenAI 兼容格式,桌面端无需任何协议适配。
|
||||
|
||||
---
|
||||
|
||||
## 7. 模块四: 系统配置迁移分析
|
||||
|
||||
### 7.1 迁移优先级分类
|
||||
|
||||
基于 `config/config.toml`、`config/chinese-providers.toml`、`config/security.toml` 分析:
|
||||
|
||||
**Critical (必须迁移)**:
|
||||
- `llm.providers[].api_key` — API 密钥应服务端管理
|
||||
- `llm.providers[].base_url` — 提供商端点集中管控
|
||||
- `llm.providers[].models[]` — 模型目录从 SaaS 下发
|
||||
- `llm.default_provider` — 默认提供商可按账号配置
|
||||
- `llm.default_model` — 默认模型可按账号配置
|
||||
|
||||
**High (重要)**:
|
||||
- `llm.requests_per_minute` / `llm.tokens_per_minute` — 按账号限流
|
||||
- `llm.max_retries` / `llm.retry_delay` — 重试策略
|
||||
- `security.auth.token_expiration` — 认证配置
|
||||
- `security.rate_limit.*` — 限流配置
|
||||
|
||||
**Medium (有益)**:
|
||||
- `agent.defaults.max_sessions` — 会话限制
|
||||
- `hands.default_approval_mode` — 默认审批模式
|
||||
- `skills.execution_timeout` — 技能超时
|
||||
|
||||
**Low (可选)**:
|
||||
- `desktop.ui.*` — UI 偏好
|
||||
- `logging.level` — 日志级别
|
||||
|
||||
**Local Only (不迁移)**:
|
||||
- `server.host/port` — 服务端特有
|
||||
- `agent.defaults.workspace` — 本地文件路径
|
||||
- `security.shell_exec.*` — 本地安全策略
|
||||
- `tools.fs.allowed_paths` — 本地文件路径
|
||||
- `logging.file.path` — 本地文件路径
|
||||
|
||||
### 7.2 同步协议
|
||||
|
||||
```
|
||||
桌面客户端 SaaS 服务端
|
||||
│ │
|
||||
│ POST /api/v1/config/sync │
|
||||
│ { action: "push", │
|
||||
│ fingerprint: "machine_xxx", │
|
||||
│ snapshot: { ... } } │
|
||||
│ ──────────────────────────────► │
|
||||
│ │ 比对 SaaS 存储
|
||||
│ { updates: [...], │
|
||||
│ conflicts: [...] } │
|
||||
│ ◄────────────────────────────── │
|
||||
│ │
|
||||
│ 应用更新 / 提示冲突 │
|
||||
│ │
|
||||
```
|
||||
|
||||
**冲突解决策略**:
|
||||
- `source: "saas"` / `"override"` → 默认 saas_wins
|
||||
- `source: "local"` → 默认 client_wins
|
||||
- 管理员可按配置项覆盖
|
||||
|
||||
### 7.3 API 端点
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | `/api/v1/migration/report` | config:read | 迁移分析报告 |
|
||||
| POST | `/api/v1/migration/analyze` | config:read | 分析配置快照 |
|
||||
| GET | `/api/v1/migration/items` | config:read | 配置项列表 |
|
||||
| PUT | `/api/v1/migration/items/:id` | config:write | 更新配置项 |
|
||||
| PUT | `/api/v1/migration/items/batch` | config:write | 批量更新 |
|
||||
| POST | `/api/v1/config/sync` | - (本人) | 客户端↔SaaS 同步 |
|
||||
| GET | `/api/v1/config/snapshot` | config:read | SaaS 配置快照 |
|
||||
| GET | `/api/v1/config/diff` | config:read | 客户端与 SaaS 差异 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 管理后台前端
|
||||
|
||||
### 8.1 技术栈
|
||||
|
||||
React 18 + TypeScript + Tailwind CSS + Recharts + React Router
|
||||
|
||||
### 8.2 页面清单
|
||||
|
||||
| 路径 | 模块 | 说明 |
|
||||
|------|------|------|
|
||||
| `/admin/login` | 通用 | 登录页 (JWT 认证) |
|
||||
| `/admin/dashboard` | 通用 | 概览仪表板 (账号数/使用量/中转状态) |
|
||||
| `/admin/accounts` | M1 | 账号列表 (搜索/角色筛选/状态筛选) |
|
||||
| `/admin/accounts/:id` | M1 | 账号详情 (编辑/查看令牌/查看日志) |
|
||||
| `/admin/roles` | M1 | 角色权限矩阵编辑 |
|
||||
| `/admin/templates` | M1 | 权限模板 CRUD + 批量应用 |
|
||||
| `/admin/logs` | M1 | 操作日志 (分页/筛选/时间范围) |
|
||||
| `/admin/profile` | M1 | 个人设置 (密码/2FA/显示名) |
|
||||
| `/admin/providers` | M2 | 提供商卡片 (状态/模型数) |
|
||||
| `/admin/providers/:id` | M2 | 提供商详情/编辑 |
|
||||
| `/admin/providers/:id/models` | M2 | 模型表格 (添加/编辑/禁用) |
|
||||
| `/admin/api-keys` | M2 | API 密钥管理 (创建/轮换/撤销) |
|
||||
| `/admin/usage` | M2 | 使用量仪表板 (图表/按提供商/按模型/按日期) |
|
||||
| `/admin/relay/dashboard` | M3 | 中转状态 (队列/提供商健康/实时指标) |
|
||||
| `/admin/relay/tasks` | M3 | 任务列表 (状态筛选/取消/重试) |
|
||||
| `/admin/migration/report` | M4 | 迁移分析报告 (分类统计/优先级图表) |
|
||||
| `/admin/migration/items` | M4 | 配置项管理 (分类/优先级/来源筛选) |
|
||||
| `/admin/config/sync-log` | M4 | 同步日志 (冲突详情/解决记录) |
|
||||
|
||||
---
|
||||
|
||||
## 9. 实施阶段
|
||||
|
||||
### Phase 1: 基础 + 账号模块
|
||||
1. `crates/zclaw-saas/` 基础结构 (Cargo.toml, db.rs, config.rs, state.rs, error.rs)
|
||||
2. `auth/` 模块 (JWT, Argon2, TOTP, 中间件)
|
||||
3. `account/` 模块 (CRUD, 角色, 模板, 操作日志)
|
||||
4. `saas-admin/` 脚手架 (登录 + 账号管理页面)
|
||||
5. 验证: 注册→登录→创建 API Token→操作日志
|
||||
|
||||
### Phase 2: 模型配置模块
|
||||
1. `model_config/` 模块 (提供商/模型 CRUD)
|
||||
2. 种子数据导入 (`chinese-providers.toml`)
|
||||
3. 账号 API 密钥管理 + 使用量统计
|
||||
4. 管理后台: 提供商/模型/API 密钥/使用量
|
||||
5. 验证: 创建提供商→添加模型→分配密钥→查看使用量
|
||||
|
||||
### Phase 3: 中转服务模块
|
||||
1. `relay/` 模块 (队列, 调度, 流式转发)
|
||||
2. 速率限制 + 重试逻辑
|
||||
3. 管理后台: 中转仪表板/任务列表
|
||||
4. 桌面端集成 (改 `base_url`)
|
||||
5. 验证: 桌面端→中转→提供商→流式响应→使用量记录
|
||||
|
||||
### Phase 4: 配置迁移模块
|
||||
1. `migration/` 模块 (配置分析, 同步协议)
|
||||
2. 种子配置项 (从 TOML 文件分析)
|
||||
3. 桌面端 `SaaSConfigClient`
|
||||
4. 更新 `KernelConfig::load()` 支持 SaaS 覆盖
|
||||
5. 验证: 配置同步→覆盖→冲突检测→解决
|
||||
|
||||
---
|
||||
|
||||
## 10. 验证方案
|
||||
|
||||
### 每阶段验证
|
||||
|
||||
1. `cargo build -p zclaw-saas` — 编译通过
|
||||
2. `cargo test -p zclaw-saas` — 单元测试通过
|
||||
3. `curl` 测试每个 API 端点
|
||||
4. 管理后台操作 → 验证数据库状态
|
||||
|
||||
### 最终端到端验证
|
||||
|
||||
1. **完整流程**: 注册账号 → 登录 → 创建 API Token → 配置提供商 → 桌面端通过中转调用模型 → 查看使用量 → 配置同步
|
||||
2. **安全验证**: JWT 过期拒绝、权限不足拒绝、API Token 撤销后拒绝、TOTP 强制验证
|
||||
3. **错误处理**: 提供商不可用降级、速率限制触发排队、队列溢出错误、配置冲突检测
|
||||
|
||||
---
|
||||
|
||||
## 11. 关键参考文件
|
||||
|
||||
| 文件 | 用途 |
|
||||
|------|------|
|
||||
| `crates/zclaw-kernel/src/config.rs` | LlmConfig/ApiProtocol 模式参考 |
|
||||
| `crates/zclaw-memory/src/schema.rs` | SQL schema 模式参考 |
|
||||
| `crates/zclaw-runtime/src/driver/openai.rs` | 中转转发实现参考 |
|
||||
| `crates/zclaw-runtime/src/driver/mod.rs` | LlmDriver trait 参考 |
|
||||
| `config/chinese-providers.toml` | 提供商种子数据源 |
|
||||
| `config/config.toml` | 配置迁移分析源 |
|
||||
| `config/security.toml` | 安全配置迁移分析源 |
|
||||
| `desktop/src-tauri/src/secure_storage.rs` | 密钥加密存储参考 |
|
||||
| `desktop/src-tauri/src/lib.rs` | Tauri 命令模式参考 |
|
||||
Reference in New Issue
Block a user