新增: df-im-gateway 网关骨架

This commit is contained in:
lxy
2026-10-05 00:35:00 +08:00
parent 91773da602
commit e492f03191
37 changed files with 4211 additions and 12 deletions
@@ -152,3 +152,194 @@ IM 用户 @机器人 "把登录任务推进到 in_review"
- **df-mcp**:对外 stdio 暴露数据层(本地进程级);本规划是远程人类入口(IM 级),互补不重叠
- **u-relay**:零改动(除非 D5 触发多 token 需求)
- **df-tunnel / AI 引擎**:零改动
## 附录A、PostgreSQL 多租户 × DevFlow 结合分析(§十 S3 展开论证)
### A.1 概念:多租户三模式
| 模式 | 结构 | 优点 | 缺点 | 适合 |
|---|---|---|---|---|
| **行级隔离** | 同库同表,每行 `tenant_id` 列,查询强制 WHERE | 成本最低、迁移跑一份、跨租户统计易 | 隔离最弱(漏写 WHERE 即泄漏)、noisy neighbor | 租户多单租户数据小,SaaS 主流 |
| **独立 Schema** | 一实例每租户一 schema | 隔离较强,按 schema 备份 | 租户多时爆炸;DDL 迁移跑 N 遍 | 租户几百内 |
| **独立 Database** | 每租户一库 | 最强隔离 | 资源贵、连接池复杂、跨租户查询难 | 租户少而大、合规严 |
行级隔离通常配合 **PG RLS(Row Level Security)**:租户过滤下沉数据库层——session 设 `SET app.tenant_id`,策略自动过滤,应用层漏写 WHERE 也不泄漏。这是 PG 多租户的核心王牌。
### A.2 激活时机
DevFlow 当前是本地优先单机应用(SQLite/单用户/零运维),「租户」不存在。多租户仅在 **S3 AI 引擎上云**时被激活:SQLite 单写者模型扛不住云端多用户并发写,且「本地优先」范式失效——隔离必须显式设计。**多租户是需求,PostgreSQL 是载体**(PG 因并发写 + RLS + 生态成为默认答案)。
### A.3 租户模型与选型
- 租户模型:`tenant`=团队(付费主体),`user`=成员;现有全部表(projects/tasks/ideas/workflows/ai_messages/knowledge/audit…)挂 `tenant_id`
- 选型:**行级隔离 + RLS**(非 schema/database 隔离)。决定性理由是迁移演进节奏:V1-V16+ 已 20+ 表且快速迭代,per-tenant schema 下每次迁移要循环跑 N 遍并处理失败重试,直接拖死迭代速度;行级隔离迁移只跑一份
### A.4 落点三抓手
1. **df-storage trait 层**(对齐 F-07 下沉思路):存储访问收敛 Repository trait,多租户化 = trait 实现层注入租户上下文(依赖 PG session 变量),上层 commands 无感——现有分层红利
2. **迁移体系**:SQLite 手写迁移换 sqlx/refinery 版本化迁移,一套脚本跑一份
3. **RLS 策略**:每表 `USING (tenant_id = current_setting('app.tenant_id')::uuid)`,连接池(pgbouncer)按事务设租户变量
### A.5 配套改造清单(多租户 ≠ 换数据库驱动)
缓存键带租户(防 Redis 串数据)/ 对象存储按租户前缀隔离 / 日志审计按租户切分 / 备份恢复按租户粒度。**全链路改造**——这正是 §十 把 PG 多租户列入「显式不做」的原因:现在做 = 为不存在的需求付全链路成本。
### A.6 与本期规划的交集
唯一交集 = P0 已预留的会话映射表 `owner_user_id` 列(S2 时是「用户归属」,S3 升级语义为「租户内用户」),成本≈0。时序红线重申:S2 各人数据仍在各自桌面端 SQLite,云端只有路由表,**不需要 PG**;S3 才引入 PG+RLS,与引擎多租户化统一做。
## 附录B、引入云端后的多人架构模式(C4 视图)
> 按 C4 模型整理:C1 上下文(现状基线 → S2 多人 → S3 引擎上云)+ C2 容器(网关内部)+ 部署视图 + 数据归属演进表。
> 贯穿不变量:**TunnelCommand/TunnelEvent 是网关↔执行端唯一契约**,执行端可从「桌面端 df-tunnel」无缝换成「云端 df-agent」,网关与 IM 端零改动。
### B.1 C1 上下文 —— 现状基线(S0:单机 + 小程序遥控)
```mermaid
flowchart TB
subgraph local["💻 用户本地(数据主权 100% 在个人)"]
desktop["DevFlow 桌面端 (Tauri v2)<br/>AI Chat 引擎(Agentic Loop+审批) · SQLite · df-tunnel(Agent)"]
end
subgraph cloud["☁️ 云端 u-relay.1216.top(nginx TLS 终止)"]
relay["u-relay :9180<br/>channel_id 配对 · payload 透传零业务耦合"]
end
mini["📱 df-miniapp 微信小程序<br/>(Commander)"]
mini <-->|"WSS"| relay
relay <-.->|"WS Agent 出站穿NAT<br/>单 channel 配对"| desktop
```
### B.2 C1 上下文 —— S2 团队多人(云端路由化,数据仍归各人)
本期 IM 网关落地后的直接形态:**云端只有「路由与审计」,业务数据仍在各桌面端**。
```mermaid
flowchart TB
subgraph users["👥 多人入口"]
direction LR
im["飞书 / 钉钉 / 企微<br/>企业机器人"]
web["Web Chat<br/>浏览器/手机"]
mini["df-miniapp<br/>微信小程序"]
end
subgraph cloud["☁️ 云端(nginx TLS 终止)"]
direction TB
gw["df-im-gateway :9190<br/>平台适配器 · 会话映射 · 白名单<br/>节流聚合 · 审批分级 · 审计"]
relay["u-relay :9180<br/>多 channel 配对转发"]
gw <-->|"WS Commander(多channel)<br/>TunnelCommand / TunnelEvent"| relay
end
subgraph deskA["用户A 本地"]
dA["DevFlow 桌面端<br/>AI引擎 + SQLite(A) · df-tunnel"]
end
subgraph deskB["用户B 本地"]
dB["DevFlow 桌面端<br/>AI引擎 + SQLite(B) · df-tunnel"]
end
im --> gw
web <-->|"HTTP/WS"| gw
mini <-->|"WSS Commander"| relay
relay <-.->|"WS Agent · chan-userA"| dA
relay <-.->|"WS Agent · chan-userB"| dB
```
**路由链路**:IM 消息 → 网关查映射表 `chat_id → (owner_user_id, channel_id, conv_id)` → 发 `TunnelCommand::Send` 到对应 channel → 该用户桌面端执行。**用户隔离靠 channel 隔离**(一人一 channel,per-channel token 为 u-relay 演进观察项)。
### B.3 C2 容器 —— df-im-gateway 内部(适配器 trait 分层,S4 扩展点)
```mermaid
flowchart TB
subgraph adapters["平台适配器层(各平台差异收敛于此)"]
feishu["feishu_adapter<br/>长连接+卡片流式"]
dingtalk["dingtalk_adapter<br/>Stream Mode"]
wecom["wecom_adapter<br/>回调验签+AES"]
webui["web_adapter<br/>WS 直连+静态页"]
end
subgraph core["核心(平台无关,S4 加端零改动)"]
unify["统一模型<br/>InboundEvent / OutboundAction"]
sess["会话映射表 (SQLite)<br/>chat_id | conv_id | owner_user_id | platform | channel_id"]
throttle["节流聚合器<br/>TextDelta 时间窗"]
acl["白名单 + 审批分级"]
audit["审计日志<br/>IM消息 ↔ TunnelCommand"]
unify --> sess
sess --> throttle
acl --> sess
sess --> audit
end
relay["u-relay WS Commander"]
feishu --> unify
dingtalk --> unify
wecom --> unify
webui <--> unify
core <-->|"TunnelCommand / TunnelEvent"| relay
```
### B.4 C1 上下文 —— S3 引擎上云(云端多人完整形态,PG 多租户)
执行端从「各人桌面端」切换为「云端 df-agent 服务」,**同一协议**,网关/IM 端零改动;桌面端退化为瘦客户端/管理台,本地优先形态仍可用(不废弃)。
```mermaid
flowchart TB
subgraph users["👥 多人入口"]
direction LR
im["飞书/钉钉/企微"]
web["Web Chat"]
end
subgraph cloud["☁️ 云端"]
direction TB
gw["df-im-gateway<br/>(零改动)"]
relay["u-relay<br/>(零改动)"]
agent["df-agent 云端执行服务<br/>AI 引擎(Agentic Loop) · 工具沙箱<br/>多会话/多租户调度"]
gw <--> relay
relay <-.->|"同协议<br/>TunnelCommand / TunnelEvent"| agent
end
subgraph data["数据层(附录A 落点)"]
pg[("PostgreSQL<br/>行级 tenant_id + RLS<br/>projects/tasks/messages/audit")]
redis[("Redis<br/>租户前缀缓存键")]
oss[("对象存储<br/>租户前缀隔离")]
end
thin["💻 桌面端=瘦客户端/管理台<br/>本地 SQLite 留个人配置"]
im --> gw
web <--> gw
agent --> pg
agent --> redis
agent --> oss
thin <-.-> relay
```
### B.5 部署视图(S2 落地形态,单机双服务 + nginx)
```mermaid
flowchart LR
subgraph inet["互联网"]
u["用户 IM / 浏览器 / 小程序"]
end
subgraph server["云端主机(u-relay 同机,复用 nginx+systemd 模式)"]
nginx["nginx :443 TLS 终止<br/>/ws/* → 9180<br/>/im/* /chat/* → 9190"]
ur["u-relay :9180<br/>systemd · musl 单二进制 ~3.6MB"]
gig["df-im-gateway :9190<br/>systemd · musl 单二进制<br/>静态 Web Chat 页托管"]
nginx --> ur
nginx --> gig
end
subgraph homes["各用户本地"]
dt["DevFlow 桌面端<br/>出站 WSS → /ws/agent"]
end
u -->|"wss://u-relay.1216.top"| nginx
dt <-.->|"出站穿NAT"| nginx
```
### B.6 数据归属与信任边界演进表
| 维度 | S0 现状 | S2 团队多人 | S3 引擎上云 |
|---|---|---|---|
| 身份/会话映射 | 桌面端 Settings | **网关 SQLite**(owner/channel 列已预留) | 网关 → 并入 PG |
| 项目/任务/工作流 | 桌面端 SQLite | **各桌面端 SQLite**(主权在个人) | PG(tenant_id+RLS) |
| AI 消息/溯源 | 桌面端 ai_messages | 各桌面端 | PG |
| IM↔指令审计 | — | 网关审计日志 | PG(租户维度) |
| 信任边界 | 个人设备 | 云端只见「指令与脱敏结果」 | 云端见全部业务数据(需租户隔离+合规) |
| 桌面端角色 | 全栈主体 | 执行主体 | 瘦客户端/管理台(可选保留本地优先) |
### B.7 演进的三条设计不变量
1. **协议不变性**:网关↔执行端唯一契约是 TunnelCommand/TunnelEvent(df-tunnel crate 类型),S3 换执行端零成本——这是全方案最重要的远期红利
2. **u-relay 纯转发零耦合**不破例:业务逻辑只进 df-im-gateway / df-agent,中继永远轻
3. **数据主权渐进上移**:S0/S2 数据在个人(本地优先不被废弃),S3 才集中上云并配套 RLS 隔离——每一步都由真实需求触发,不过度设计