feat: integrate platform backend and application interfaces
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
# MiragenFlow 架构分析
|
||||
|
||||
## 1. 系统定位
|
||||
|
||||
MiragenFlow 是一个本地优先的 AI 创作工作台,并在服务端提供统一的任务网关、计费、渠道路由和管理能力。
|
||||
|
||||
- 用户端负责画布编排、图片/文本/音频创作、任务查看、资产管理和 WebDAV 同步。
|
||||
- 管理台负责用户、渠道、模型产品、套餐、计费、审批、任务、存储、指标和审计。
|
||||
- 服务端负责认证、目录、任务、余额、队列、供应商调用、对象落地、事件和后台作业。
|
||||
- `packages/contracts` 维护浏览器和服务端共享的会话、任务、事件、上传对象等 TypeScript 合约。
|
||||
|
||||
当前系统不是“浏览器直接持有供应商 Key”的纯前端工具:计费任务默认通过 `/api/v1` 服务端网关执行。另一方面,画布项目和“我的资产”仍主要保存在浏览器本地,WebDAV 是可选同步层,不是平台自带云端项目库。
|
||||
|
||||
## 2. 运行时边界
|
||||
|
||||
```text
|
||||
用户端 web/ ─┐
|
||||
├─ shared contracts ─> server HTTP /api/v1
|
||||
管理台 admin/ ┘ │
|
||||
├─ Auth / CSRF / MFA / Scope
|
||||
├─ Store + PersistenceRepository
|
||||
├─ Task Dispatch Outbox + Queue
|
||||
├─ Task Worker + Provider Adapter
|
||||
├─ Staging / Object / Asset
|
||||
├─ Task WebSocket Events
|
||||
└─ Message / WebDAV / GC / Reconcile Workers
|
||||
```
|
||||
|
||||
服务端启动时加载配置、迁移和持久化状态,恢复任务租约、消息 outbox、WebDAV 作业和排队任务,再启动 HTTP、任务 WebSocket 及周期性恢复/GC 作业。证据入口:`server/src/index.ts:13`。
|
||||
|
||||
## 3. 用户端架构
|
||||
|
||||
### 3.1 页面和启动
|
||||
|
||||
- `web/src/main.tsx:1` 组合 `AppProviders` 与 `RouterProvider`。
|
||||
- `web/src/router.tsx:29` 集中注册公开页面、认证页面和受保护工作台路由。
|
||||
- `AuthGuard` 在认证 store 完成 hydrate 前保持加载态,未登录则跳到登录页并保留 redirect。
|
||||
- `ClientRootInit` 启动时并行恢复会话并加载公开模型目录。
|
||||
|
||||
### 3.2 状态与本地持久化
|
||||
|
||||
- `useAuthStore` 管理内存 access token、用户、余额、MFA challenge 和 refresh 恢复。
|
||||
- `useCatalogStore` 缓存 `/api/v1/catalog/models` 的公开模型目录。
|
||||
- `useCanvasStore` 通过 localForage 持久化项目,约 400ms 防抖写入。
|
||||
- `useAssetStore` 持久化文本、图片和音频资产,并在删除时清理无引用媒体。
|
||||
- `local-data-namespace` 以用户 ID 或 anonymous 区分本地命名空间,并用 epoch 防止切换竞态。
|
||||
|
||||
因此账号登录和服务端任务是云端边界,画布及本地资产不是。清理站点数据仍可能丢失未同步的本地内容。
|
||||
|
||||
### 3.3 API 与任务体验
|
||||
|
||||
`web/src/services/api/platform.ts:81` 是统一请求边界:
|
||||
|
||||
- 同源 `/api/v1` 请求带 `credentials: include`。
|
||||
- access token 只放内存并以 Bearer 发送。
|
||||
- Cookie 会话存在时,写请求附带 CSRF token。
|
||||
- 401 时只刷新一次,失败后触发统一会话过期处理。
|
||||
|
||||
任务创建链路位于同文件 `runPlatformTask`:创建任务后优先订阅 `/api/v1/ws/tasks`;WebSocket 失败时以 250ms 轮询任务详情。任务页另以 5 秒可见刷新维护列表。
|
||||
|
||||
### 3.4 上传、对象和 WebDAV
|
||||
|
||||
- 小于等于 50MB 的图片可走 multipart;其他对象走预签名/complete 链路。
|
||||
- 任务结果通过带认证的 `/api/v1/objects/*` 读取。
|
||||
- `app-sync.ts` 在画布/资产 hydration 后合并远端 manifest,补齐媒体并上传变化。
|
||||
- WebDAV 同步是用户主动配置的跨设备同步,不是多人协作或实时云存储。
|
||||
|
||||
## 4. 管理台架构
|
||||
|
||||
### 4.1 入口、状态和路由
|
||||
|
||||
- `admin/src/main.tsx:3` 组合 Redux `Provider`、BrowserRouter 和应用入口。
|
||||
- Redux 只维护 `global` 与 `user` 两个 slice;业务列表不进入大型全局 store。
|
||||
- `admin/src/router/index.ts:36` 为路由声明 `requiredScope`。
|
||||
- 用户安全、财务、审计等页面分别需要 `admin:security`、`admin:finance`、`admin:audit`;其他页面默认 `admin:read`。
|
||||
- `PermissionGate` 读取角色和 scope,`super_admin` 直接通过,其余不足权限时进入 `/403`。
|
||||
|
||||
### 4.2 请求与业务页
|
||||
|
||||
`admin/src/services/platform.ts:65` 的 `adminRequest` 统一处理:
|
||||
|
||||
- Bearer access token、管理员 CSRF Cookie、`credentials: include`。
|
||||
- 非 GET 请求自动生成 Idempotency-Key。
|
||||
- 401 只触发一次并发去重的 refresh。
|
||||
- 错误对象保留 status、code、details 和 requestId。
|
||||
|
||||
当前大多数业务路由懒加载 `admin/src/pages/Business/index.tsx`。该页面根据 section 映射读取 `/api/v1/admin/*`,写操作统一处理 `If-Match`、幂等、刷新列表及 409/422/503。
|
||||
|
||||
管理台的“实时指标”并非 WebSocket 客户端:页面每 10 秒 GET `/api/v1/admin/ws/metrics`,服务端即时聚合订阅数、队列和事件积压。审计大板和详细日志都读取 `/api/v1/admin/audit-logs`。
|
||||
|
||||
## 5. HTTP、安全与权限
|
||||
|
||||
HTTP 入口在 `server/src/app/http.ts:1174`:
|
||||
|
||||
- 每个请求分配 requestId,并统一处理 CORS、body limit、健康和就绪检查。
|
||||
- `/api/v1` 写请求先经过 CSRF 检查;纯 Bearer 且没有 refresh Cookie 的请求不强制双重提交。认证引导端点可以恢复缺少 CSRF Cookie 的旧 refresh 会话并补发缺失 Cookie,普通业务写请求仍受双提交保护。
|
||||
- 用户 token 需要校验 JWT、session family、撤销状态和账号状态。
|
||||
- 管理 token 使用独立 audience、角色和 scopes。
|
||||
- 管理资源 scope 由 `adminRequiredScope` 统一映射,不依赖前端菜单作为安全边界。
|
||||
- 非读取管理请求必须带合法 Idempotency-Key,并以 body fingerprint 防止同键异体重放。
|
||||
|
||||
密码使用 scrypt;JWT 使用 HS256 并包含 aud、scope、jti、sid、roles/scopes 等声明。登录策略还支持 CAPTCHA、TOTP 和 recovery code。
|
||||
|
||||
生产配置会强制检查 JWT secret、管理员密码、渠道加密密钥、PostgreSQL 和 Redis 配置;本地开发允许 memory/file persistence 与 memory queue。
|
||||
|
||||
## 6. 任务主链
|
||||
|
||||
### 6.1 创建与预留
|
||||
|
||||
`server/src/app/http.ts:751` 的任务创建先完成:
|
||||
|
||||
1. 校验公开模型、能力、输入引用、mask、数量、分辨率和参数。
|
||||
2. 解析产品、价格、渠道组和套餐权益快照。
|
||||
3. 计算预计金币并检查并发限制。
|
||||
4. 在账本中把 available 移到 reserved。
|
||||
5. 原子写入 task、幂等记录、task event、dispatch outbox 和队列信息。
|
||||
6. 返回 `202` 与公开任务投影。
|
||||
|
||||
### 6.2 队列和租约
|
||||
|
||||
`server/src/infra/queue.ts` 定义统一队列接口。开发可使用内存队列;生产 Redis 适配器使用 sorted set、hash 和 Lua 原子 claim/renew/nack,并维护 dead-letter 集合。
|
||||
|
||||
PostgreSQL 模式使用 task dispatch outbox:事务提交后再由 dispatcher 以 `SKIP LOCKED` claim 并投递唯一 taskId,避免“数据库已提交但队列未写入”的双写问题。
|
||||
|
||||
### 6.3 Worker、重试和未知结果
|
||||
|
||||
`server/src/jobs/task-worker.ts:400`:
|
||||
|
||||
- Worker claim durable lease 后把 queued 改为 running,并周期性续租。
|
||||
- 路由快照生成按渠道优先级、重试预算和断路器状态排列的 attempt plan。
|
||||
- 每个 attempt 带平台幂等键,调用 provider adapter 后分类为 success、retryable、permanent 或 unknown。
|
||||
- 可重试错误切换渠道;预算耗尽才失败并释放剩余预留。
|
||||
- 网络超时、租约丢失或结果不确定不会直接重复请求,而是进入 unknown/reconciliation,防止供应商已完成但平台重复扣费。
|
||||
- 续租失败会 fence 后续副作用,避免两个 Worker 同时结算同一任务。
|
||||
|
||||
### 6.4 输出与结算
|
||||
|
||||
成功输出先写 staging/object,再创建用户 asset 和 output 记录;结算按实际成功输出和价格快照扣除 reserved,释放多余预留。结算失败会进入 unknown,等待一致性恢复。
|
||||
|
||||
## 7. 供应商适配
|
||||
|
||||
`server/src/adapters/provider.ts` 是供应商边界:
|
||||
|
||||
- 平台显示模型 ID 与供应商请求模型 ID 分开解析,模型映射只在出站时生效。
|
||||
- OpenAI 兼容图片适配器支持 `/images/generations`、`/images/edits`、多参考图、mask、参数和 idempotency header。
|
||||
- 出站 URL 在生产要求 HTTPS 与公网 DNS;请求固定解析地址、禁用重定向,并限制超时和响应字节数。
|
||||
- transport/timeout 归类为 unknown,而不是轻率判为 failed。
|
||||
- admin 模型探测会明确请求 `/v1/models` 并返回 requestAttempted 与分类诊断。
|
||||
|
||||
设计文档中的 `@图片X` 文本重写、固定 1K/2K/4K 模型后缀和比例矩阵属于产品设计要求;当前源码已确认参考图数组、mask、分辨率模型映射和 OpenAI 兼容调用,但本次没有在服务端调用链中确认独立的 `@图片X -> 第X张图` 文本重写器。
|
||||
|
||||
## 8. 一致性、持久化与数据
|
||||
|
||||
### 8.1 Store 与事务
|
||||
|
||||
Store 聚合用户、会话、模型、渠道、任务、attempt、事件、余额 bucket、ledger、订单、审批、审计和 WebDAV 状态。
|
||||
|
||||
- 内存事务保存 snapshot,失败时回滚,提交后才发布缓冲事件。
|
||||
- 文件适配器使用临时文件 + rename 原子替换,并以 0600 权限写入。
|
||||
- PostgreSQL Repository 使用 revision/advisory lock、乐观冲突重试和领域行投影。
|
||||
- task lease、dispatch outbox、message outbox 和 WebDAV job 都有独立 durable claim/renew/complete 接口。
|
||||
|
||||
### 8.2 余额与审批
|
||||
|
||||
账本使用 bucket slice 记录 reserve、settle、release、refund 和 adjustment。人工充值、退款及高风险调账先创建审批单;默认禁止申请人自批,执行时可要求管理员 TOTP,并把账本、订单、审批状态和审计放入同一事务。
|
||||
|
||||
支付 webhook 校验签名、金额、币种和唯一 eventId;订单、payment event、余额 bucket 和 ledger 原子提交。非生产 mock 支付与真实 adapter 路径明确分开。
|
||||
|
||||
### 8.3 审计
|
||||
|
||||
审计 helper 会脱敏凭证、provider 内部标识和路由快照,并用 previousHash/hash 形成链式校验。管理读审计列表本身也会写入 `audit.read`,因此审计读取是可追踪操作。
|
||||
|
||||
## 9. 事件、指标与后台作业
|
||||
|
||||
### 9.1 用户任务事件
|
||||
|
||||
`server/src/app/ws.ts` 只接受 `/api/v1/ws/tasks`:
|
||||
|
||||
- 校验同源与用户会话;生产拒绝 query access_token。
|
||||
- 每连接最多 8 个任务订阅,20 秒 heartbeat。
|
||||
- 订阅时先按 cursor 回放,再接收实时事件。
|
||||
- 以 eventId 和每任务 sequence 去重。
|
||||
- HTTP 事件与 WS 事件都经过 `publicTaskEvent` 白名单投影,隐藏 channel/provider 等敏感字段。
|
||||
|
||||
### 9.2 后台作业
|
||||
|
||||
服务端还运行消息 outbox、WebDAV sync/retention、staging GC、租约恢复、reservation 恢复和 unknown reconciliation。WebDAV 适配器校验公网 HTTPS、清理路径穿越,并使用 ETag、租约、指数退避和冲突副本。
|
||||
|
||||
## 10. 部署形态
|
||||
|
||||
| 环境 | 持久化 | 队列 | 外部依赖 |
|
||||
| --- | --- | --- | --- |
|
||||
| 本地开发 | memory/file | memory | 可使用本地 fixture,但不会自动创建展示渠道 |
|
||||
| 生产 | PostgreSQL | Redis | 公网 HTTPS provider、真实支付/消息/WebDAV 配置 |
|
||||
|
||||
已确认的生产硬要求来自 `server/src/config.ts:76`。`system-diagrams.md` 提到可选 S3,但当前源码中实际确认的是本地 staging/object 处理;不能把外部 S3 视为已经实现。
|
||||
|
||||
## 11. 已确认事实与未确认边界
|
||||
|
||||
### 已确认
|
||||
|
||||
- 用户端、管理台、共享合约和服务端是四个明确代码边界。
|
||||
- 任务采用余额预留、dispatch outbox、队列 lease、attempt、重试/断路器和 unknown 对账。
|
||||
- 用户任务事件有 WS + cursor replay + HTTP fallback。
|
||||
- 管理台实时指标为 HTTP 轮询。
|
||||
- 画布与本地资产以 localForage/IndexedDB 为主,WebDAV 可选同步。
|
||||
- 生产要求 PostgreSQL 与 Redis。
|
||||
|
||||
### 未确认或不应过度承诺
|
||||
|
||||
- 没有确认外部 S3 对象存储实现;当前应按本地 staging/object 理解。
|
||||
- 没有确认设计文档中的 `@图片X` 服务端重写器已经落地。
|
||||
- 没有确认真正的 3D/视频生成;现有范围以图片、文本、音频和 2D DOM/SVG 画布为主。
|
||||
- AGENTS.md 提到的 Agent 对话 `threadId/turnId/itemId` 协议未在当前 `server/` 与 `packages/contracts/` 中找到实现。
|
||||
- 多实例扩展仍依赖 PostgreSQL/Redis、租约 fencing 和外部服务的生产验证,不能以本地 fixture 代替。
|
||||
|
||||
## 12. 图示产物
|
||||
|
||||
- `docs/miragenflow-architecture.html`:全平台组件、边界、请求和异步主链。
|
||||
- `docs/miragenflow-task-lifecycle.html`:任务状态主线、失败重试、unknown 保护和取消出口。
|
||||
- 对应 JSON 规格位于同目录,可继续用 Archify 验证和生成。
|
||||
Reference in New Issue
Block a user