234 lines
14 KiB
Markdown
234 lines
14 KiB
Markdown
# EveryPublish 系统设计(网页端 + 客户端 App)
|
||
|
||
> 版本 v1 · 2026-08-20 · 与 docs/multi-platform-publish-plan.md 配套(架构原则见其 §3/§3.1/§17-22)
|
||
|
||
## 1. 总体形态
|
||
|
||
- **网页端(我方统一部署)**:客户工作台 + 内部后台,同一应用、角色与租户隔离;客户与我们内部共用一套代码。
|
||
- **客户端 App(客户部署)**:Agent 常驻应用(系统托盘/后台服务),承载数据面。
|
||
- **连接**:WSS 双向长连接为主通道 + HTTPS 长轮询降级(评估见 §4)。
|
||
|
||
> **术语澄清**:本文「Agent」= 客户端发布执行器(本地常驻程序,系统托盘/后台服务形态),与 AI 大模型 Agent 无关——不跑模型、不调 AI 接口、不需要任何模型配置。用户侧零配置:下载安装包 → 扫码绑定 → 完成。运行时与浏览器驱动全部内置进安装包。对外可称「发布助手」。
|
||
|
||
## 2. 网页端功能清单
|
||
|
||
### 2.1 客户工作台(按角色)
|
||
|
||
| 模块 | 功能点 | 可见角色 |
|
||
|---|---|---|
|
||
| 认证与账户 | 登录(密码+TOTP 2FA)、找回密码、登录设备管理 | 全部 |
|
||
| 工作区与成员 | 成员邀请、角色(管理员/审核/运营)、权限 | 管理员 |
|
||
| 平台账号台账 | 绑定/解绑、登录态健康度(过期预测)、代理绑定查看、发起扫码 | 管理员 |
|
||
| 素材库 | 上传(OSS 预签名直传)、分组/标签、检索、预览、元信息 | 运营/审核 |
|
||
| 发布任务 | 创建(选素材/平台/账号/标题话题/定时)、排期日历、列表筛选、实时状态、失败重试、取消 | 运营 |
|
||
| 审核流 | 待审队列、通过/驳回+批注(可配置跳过) | 审核/管理员 |
|
||
| 通知与挑战 | 挑战处理中心(扫码/验证码/APP确认)、失败告警、登录过期提醒 | 管理员/运营 |
|
||
| 审计 | 全操作日志查询 | 管理员 |
|
||
| 数据报表(可选) | 发布量/成功率/失败原因分布 | 管理员 |
|
||
|
||
### 2.2 内部后台(平台角色,与工作台同部署)
|
||
|
||
| 模块 | 功能点 |
|
||
|---|---|
|
||
| 租户管理 | 客户列表、套餐/计费、配额、状态 |
|
||
| Agent 管理 | 设备在线监控、版本推送、远程指令(重启/升级)、强制下线/吊销 |
|
||
| 渠道适配器 | 平台模块状态、改版巡检、灰度开关 |
|
||
| 风控台账 | 风控事件、账号冷却记录 |
|
||
| 系统配置 | 通知模板、代理池(托管形态)、审计查询 |
|
||
|
||
## 3. 客户端 App 功能清单
|
||
|
||
| 模块 | 功能点 |
|
||
|---|---|
|
||
| 安装与配对 | 安装包、配对码绑定工作区、首次引导 |
|
||
| 连接管理 | WSS 连接状态、心跳、自动重连、延迟诊断 |
|
||
| 凭据保险库 | 本地加密存储(SQLCipher)、账号列表与健康度 |
|
||
| 扫码与挑战 | 二维码展示/自动刷新、验证码回填、APP确认轮询 |
|
||
| 代理管理 | 代理配置、健康检查、按账号绑定 |
|
||
| 任务执行 | 接收/幂等去重/执行/回传;浏览器档案管理;指纹配置 |
|
||
| 运维 | 本地日志、自动更新、远程指令(重启/升级/日志上传) |
|
||
|
||
## 4. 网页端与 App 的连接方案评估
|
||
|
||
| 方案 | 延迟 | 双向 | 穿透 | 安全 | 结论 |
|
||
|---|---|---|---|---|---|
|
||
| REST 轮询 | 秒级 | 否 | 是 | 中 | 仅作降级通道 |
|
||
| 服务器回调 Agent(Agent 开端口) | 低 | 是 | 否(需端口映射/UPnP) | 差(暴露面) | 排除 |
|
||
| SSE 单向推送 | 低 | 半 | 是 | 中 | 可选补充 |
|
||
| MQTT | 低 | 是 | 是(出站) | 高 | 进阶可选(暂不引入) |
|
||
| **WSS(Agent 出站)** | **<50ms** | **是** | **是(免内网穿透)** | **高** | **主通道** |
|
||
| gRPC 双向流 | 低 | 是 | 是 | 高 | 暂不需要 |
|
||
|
||
**安全依据(为什么 WSS 出站最安全)**:
|
||
|
||
1. **零入站暴露面**:Agent 不监听任何端口,攻击者无处下手。
|
||
2. **设备级身份**:安装时生成 Ed25519 密钥对,配对时注册公钥;连接与关键消息均签名,不可伪造。
|
||
3. **单一信任锚**:Agent 只信任我方服务器;所有指令经服务器签名下发,杜绝中间人。
|
||
4. **可降级**:企业防火墙拦截 WSS 时自动降级 HTTPS 长轮询(同样 TLS+签名),可用性不降级。
|
||
5. **证书固定(可选)**:TLS 1.3 + 证书指纹固定,防内网 DNS 劫持。
|
||
|
||
## 5. 鉴权体系(四层)
|
||
|
||
| 对象 | 方式 | 凭证 | 存储 | 吊销 |
|
||
|---|---|---|---|---|
|
||
| 网页用户 | 密码(Argon2id)+ TOTP 2FA(可选,默认关闭,管理员可开启) | Access JWT 15min + Refresh Cookie 7d(轮换) | 服务器 | 会话管理/登出全部设备 |
|
||
| Agent 设备 | 配对码 + Ed25519 签名 | agentToken(长期可吊销) | 服务器存公钥;App 本地存私钥(DPAPI/SQLCipher) | 控制台吊销设备 |
|
||
| 平台官方 API(X/IG/YouTube) | OAuth2 + PKCE | refresh token | 服务器 KMS 信封加密 | OAuth 应用撤销 |
|
||
| 国内平台账号 | 官方扫码登录 | cookie/token | 仅 Agent 本地保险库,服务器零凭据 | 远程失效→重新扫码 |
|
||
| 挑战输入(验证码等) | WSS 加密通道 | 不适用 | 不落日志、不持久化明文 | 会话结束即弃 |
|
||
|
||
## 6. REST API 清单(网页端 → 服务器)
|
||
|
||
| 模块 | 端点 | 说明 |
|
||
|---|---|---|
|
||
| 认证 | POST /api/auth/login、/refresh、/logout、/2fa/verify | 登录/刷新/登出/两步验证 |
|
||
| 工作区 | GET/PUT /api/workspace、POST /api/members/invite、PATCH /api/members/:id | 工作区与成员 |
|
||
| 账号台账 | GET /api/accounts、POST /api/accounts/bind、DELETE /api/accounts/:id、GET /api/accounts/:id/health | 绑定发起即生成挑战 |
|
||
| 素材 | GET/POST /api/materials、POST /api/materials/presign、DELETE /api/materials/:id | 直传签名与元数据 |
|
||
| 任务 | GET/POST /api/tasks、POST /api/tasks/:id/submit/approve/reject/retry/cancel、GET /api/tasks/:id/events | 任务生命周期 |
|
||
| 挑战 | GET /api/challenges、POST /api/challenges/:id/resolve | resolve 载荷:验证码/确认结果 |
|
||
| 审计 | GET /api/audit-logs | 全操作日志 |
|
||
| 通知 | GET /api/notifications、POST /api/notifications/read | 通知中心 |
|
||
| Agent 管理 | POST /api/agents/pairing、GET /api/agents、POST /api/agents/:id/revoke、POST /api/agents/:id/cmd | 配对码/吊销/远程指令 |
|
||
| 内部后台 | GET /api/admin/tenants、/agents、/adapters、/risk-events | 平台角色专属,租户域隔离 |
|
||
|
||
## 7. WebSocket 消息协议
|
||
|
||
| 方向 | 消息 | 载荷要点 |
|
||
|---|---|---|
|
||
| A→S | hello | deviceId + 签名 + 协议版本(连接即鉴权,只此一次) |
|
||
| A→S | heartbeat | 10s 间隔;服务器 25s 未收判定离线 |
|
||
| S→A | task.push | taskId + 加密载荷(素材URL/标题/平台/账号) |
|
||
| A→S | task.ack / task.result | 幂等确认 / 结果+截图URL+失败原因分类 |
|
||
| A→S | challenge.push | 类型(qr/confirm/sms/captcha)+ 凭证 |
|
||
| S→A | challenge.resolve | 用户输入结果(验证码等,端到端加密) |
|
||
| S→A | agent.cmd / config.update | 重启/升级/配置下发(签名) |
|
||
| 浏览器↔S | 原生 WebSocket(JSON 协议) | task / challenge / notification 订阅推送(JWT 鉴权) |
|
||
|
||
## 8. 关键流程
|
||
|
||
### 8.1 网页用户登录(标准)
|
||
|
||
账号密码 → 签发 Access JWT(15min) + Refresh Cookie(7d 轮换) → 进入工作区。TOTP 2FA 为可选能力,**默认关闭**,管理员可对工作区开启;异常登录(新设备/异地)触发告警通知。
|
||
|
||
### 8.2 Agent 配对(一次性)
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant U as 用户
|
||
participant W as 网页端
|
||
participant S as 服务器
|
||
participant A as App(Agent)
|
||
U->>W: 登录(密码+TOTP 2FA)
|
||
W->>S: POST /api/auth/login
|
||
S-->>W: JWT(15min) + refresh cookie
|
||
U->>W: 工作区→设备→添加设备
|
||
W->>S: POST /api/agents/pairing
|
||
S-->>W: 配对码(6位,5分钟有效)
|
||
U->>A: 输入配对码
|
||
A->>A: 生成Ed25519密钥对+deviceId
|
||
A->>S: WSS auth(deviceId,配对码,公钥,签名)
|
||
S->>S: 验证→注册公钥→签发agentToken(可吊销)
|
||
S-->>A: 绑定成功
|
||
A->>A: 私钥入本地保险库(DPAPI/SQLCipher)
|
||
```
|
||
|
||
### 8.3 平台账号绑定(沿用 §22 优化登录)
|
||
|
||
控制台发起绑定 → 服务器生成挑战 → Agent API 取码(B站等)或温浏览器取码(视频号)→ 只传 token/URL → 控制台重渲染 → 用户扫码 → Agent 短轮询检测 → 换取 cookie 入保险库 → 台账变绿。
|
||
|
||
### 8.4 发布任务全链路(沿用 §21 全链路时序)
|
||
|
||
|
||
## 9. 边界问题与解决方案
|
||
|
||
| 问题 | 解决方案 |
|
||
|---|---|
|
||
| 企业防火墙拦截 WSS | 443 端口 + wss;仍失败则自动降级 HTTPS 长轮询(同签名) |
|
||
| 服务器重启/网络抖动 | Redis 持久队列(asynq)+ 重投 + Agent 断线重连 + taskId 幂等 ack |
|
||
| 重复执行 | taskId 全局唯一,Agent 本地状态表去重 |
|
||
| 大文件挤占消息通道 | 素材走 OSS 预签名直传,WSS 只传元数据 + SHA-256;断点续传 |
|
||
| 两端时钟不同步(签名 nonce) | 配对时同步服务器时间 + nonce 宽限窗口 |
|
||
| 并发挑战冲突 | 账号级互斥锁:一账号同一时刻仅一个挑战 |
|
||
| 版本不兼容 | 协议 version 字段 + zod schema 校验,旧版 App 只读降级 |
|
||
| 验证码泄露 | 挑战消息端到端加密、日志脱敏、不持久化明文 |
|
||
| 排期时区错乱 | 统一 UTC 存储,控制台按本地时区渲染 |
|
||
| 托管形态代理失效 | 池健康检查自动摘除 + 同区域切换 + 告警 |
|
||
|
||
## 10. 核心数据模型
|
||
|
||
> 数据库引擎:MySQL 8.x(GORM);Redis 承载队列与缓存(asynq)。
|
||
|
||
| 表 | 关键字段 | 说明 |
|
||
|---|---|---|
|
||
| tenant | id, name, plan, status | 租户(客户/内部) |
|
||
| user | id, tenant_id, phone/email, password_hash, totp_secret, role | 平台角色独立域 |
|
||
| platform_account | id, tenant_id, platform, status, health, proxy_binding | 台账(无凭据字段) |
|
||
| agent_device | id, tenant_id, device_id, public_key, token_hash, status, version | 设备(公钥,无私钥) |
|
||
| material | id, tenant_id, oss_key, sha256, meta | 素材 |
|
||
| task | id, tenant_id, material_id, targets, schedule_at, status, retry_count | 发布任务 |
|
||
| task_event | id, task_id, type, payload, ts | 状态事件流 |
|
||
| challenge | id, account_id, type, status, expires_at | 挑战(凭证加密) |
|
||
| audit_log | id, tenant_id, actor, action, target, ts | 审计 |
|
||
| notification | id, tenant_id, user_id, channel, content, read_at | 通知 |
|
||
|
||
## 11. 安全清单
|
||
|
||
- 全链路 TLS 1.2+(HTTPS/WSS);密码 Argon2id;登录限速;异常登录告警;TOTP 2FA 可选(默认关闭)。
|
||
- 租户数据行级隔离(tenant_id 强制注入);内部后台独立角色域。
|
||
- 敏感字段 KMS 信封加密;日志脱敏;挑战内容不落日志。
|
||
- Agent 指令签名校验;设备可远程吊销。
|
||
- 依赖扫描 + 常规安全审计(SAST/依赖 CVE)。
|
||
## 12. 传输体系设计
|
||
|
||
### 12.0 分期策略(一期本地,二期接桶)
|
||
|
||
- **一期(首版)**:不接入存储桶。素材存服务器本地磁盘;运营 multipart 直传服务器;Agent 用**带短时效签名 token 的直链下载**(HTTP 直链,不做分片续传)。大文件同样不走 WSS。
|
||
- **二期**:接 OSS 双桶(国内/海外)、预签名 URL、Range 分片断点续传、本地缓存 LRU(即 §12.1-12.5 全部能力)。
|
||
- **工程要求**:存储层必须抽象为 StorageDriver 接口(一期 local 实现 / 二期 oss 实现),上传、下载、元数据三处调用全部经接口——二期切换零业务改动。
|
||
|
||
### 12.1 三链路分离(控制面轻、数据面直)
|
||
|
||
| 链路 | 内容 | 通道 | 典型大小 | 续传 | 加密 |
|
||
|---|---|---|---|---|---|
|
||
| 控制面 | 任务/状态/挑战/心跳 | WSS | <1KB | 幂等重发 | TLS + 签名 |
|
||
| 数据面·上传 | 运营浏览器 → 素材桶 | HTTPS 直传(预签名) | 10MB ~ 数GB | 分片续传 | TLS + 私有桶签名 |
|
||
| 数据面·下发 | 素材桶 → Agent | HTTPS Range 下载 | 10MB ~ 数GB | Range 续传 | TLS + 短时效签名 |
|
||
| 数据面·回传 | 截图 → 素材桶 | HTTPS 直传(预签名) | 100-500KB | 无需 | TLS + 签名 |
|
||
|
||
**铁律:大文件绝不走 WSS**(避免阻塞消息通道);WSS 只传元数据与短时效下载 URL。一期由服务器本地盘存素材字节(单机磁盘即够);二期切换 OSS 后服务器只存元数据(oss_key/sha256/大小)。
|
||
|
||
### 12.2 全链路传输流程
|
||
|
||
> 注:图中「素材桶 OSS」为二期形态;一期将 OSS 替换为服务器本地盘(见 §12.0 分期策略),其余链路不变。
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
OP["运营(网页端)"] -->|"① 分片直传(预签名,断点续传)"| OSS["素材桶 OSS"]
|
||
OP -->|"② 元数据 API(oss_key/sha256)"| S["服务器"]
|
||
S -->|"③ WSS task.push + 短时效下载URL"| A["Agent"]
|
||
A -->|"④ Range分片下载+SHA256校验+本地缓存"| OSS
|
||
A -->|"⑤ 上传发布"| P["目标平台"]
|
||
P -->|"⑥ 回执+截图"| A
|
||
A -->|"⑦ 截图直传OSS(预签名)"| OSS
|
||
A -->|"⑧ WSS task.result(URL+状态)"| S
|
||
S -->|"⑨ WSS 实时状态推送"| OP
|
||
```
|
||
|
||
### 12.3 断点续传与完整性
|
||
|
||
- 浏览器上传:OSS 分片上传(SDK 原生),失败自动重传分片。
|
||
- Agent 下载:HTTP Range 分段 + 本地 .part 文件,完成后整体 SHA-256 校验(与 DB 元数据比对),失败重下。
|
||
- 预签名 URL 过期:Agent 向服务器换新 URL(协议预留 refresh 消息)。
|
||
|
||
### 12.4 区域与成本
|
||
|
||
- **双桶架构**:国内桶(阿里云 OSS/腾讯 COS)+ 海外桶(Cloudflare R2 或 S3),按任务目标平台就近分配——国内平台任务用国内桶、国际平台任务用海外桶,避免跨境传输慢。
|
||
- **本地缓存 + LRU**:重复任务/重试不重复下载,降低 OSS 流量成本。
|
||
- 进阶:大客户可绑定自有存储(数据不出门)。
|
||
|
||
### 12.5 安全
|
||
|
||
- 私有桶 + 预签名 URL(上传 15min、下载 2h 短时效);URL 只经加密通道下发,不落日志。
|
||
- 上传白名单:仅视频/图片 MIME + 大小上限。
|
||
- 素材本身最终要公开到平台,HTTPS 即够;凭据/挑战类数据才端到端加密。 |