Files
MiragenFlow/docs/miragenflow-architecture-analysis.md

13 KiB
Raw Permalink Blame History

MiragenFlow 架构分析

1. 系统定位

MiragenFlow 是一个本地优先的 AI 创作工作台,并在服务端提供统一的任务网关、计费、渠道路由和管理能力。

  • 用户端负责画布编排、图片/文本/音频创作、任务查看、资产管理和 WebDAV 同步。
  • 管理台负责用户、渠道、模型产品、套餐、计费、审批、任务、存储、指标和审计。
  • 服务端负责认证、目录、任务、余额、队列、供应商调用、对象落地、事件和后台作业。
  • packages/contracts 维护浏览器和服务端共享的会话、任务、事件、上传对象等 TypeScript 合约。

当前系统不是“浏览器直接持有供应商 Key”的纯前端工具:计费任务默认通过 /api/v1 服务端网关执行。另一方面,画布项目和“我的资产”仍主要保存在浏览器本地,WebDAV 是可选同步层,不是平台自带云端项目库。

2. 运行时边界

用户端 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 验证和生成。