Files
hi-server/docs/simnet-implementation-plan.md
T
shanshanzhong147 634b5a7bd0 feat(simnet): add SimNet protocol end-to-end support
- model: SimNet protocol fields + NormalizeSimnet; type+port protocol uniqueness
- server config: OmnXT runtime config delivery via compatible() simnet case
- credentials: derive per-user psk/key_id from subscription (pkg/simnet), no new table
- subscription: adapter buildOmnxtSimnetConfigs + base64 buildOmnxtProtocolLinks + OmnXT SimNet application (migration 02161)
- UA gating: hide experimental protocols from non first-party clients (download + JSON node-list)
- admin: normalize simnet on create/update and on GET responses
- tests: 21 simnet unit tests; full suite green
2026-07-27 00:17:02 -07:00

32 KiB
Raw Blame History

PPanel Server Simnet 协议接入实施计划

本文档用于指导在现有自维护后端 /Users/Apple/code_vpn/vpn/ppanel-server 中接入 simnet 协议。目标不是把 Pro 新版后端整体迁移进来,而是在保留旧系统架构、数据库主链路和现有节点管理模型的前提下,把 simnet 做到管理端可配置、OmnXT 节点可拉取、SlagClient 可订阅连接、用户授权和流量统计闭环。

参考实现来自新版 Pro 后端:/Users/Apple/Downloads/NPanelPro-pro/NPanel-backend

1. 项目背景

当前旧后端已经有完整的 Server、Node、Subscribe、Traffic、Online User 等链路,协议配置主要保存在 Server 的 protocols JSON 字段里,Node 侧用 protocol + port + address 描述对外节点。新版 Pro 后端已经加入了 simnet 协议字段、管理端接口、节点兼容接口和订阅交付逻辑,但它的整体工程结构和旧仓库不同。

旧仓库是 Gin/goctl/Gorm 风格,核心入口包括:

  • API 定义:apis/admin/server.apiapis/node/node.apiapis/public/subscribe.apiapis/types.api
  • 生成类型:internal/types/types.go
  • 管理端 Server 逻辑:internal/logic/admin/server/*
  • 节点服务端配置拉取:internal/logic/server/getServerConfigLogic.go
  • 节点用户列表拉取:internal/logic/server/getServerUserListLogic.go
  • 公共订阅节点返回:internal/logic/public/subscribe/queryUserSubscribeNodeListLogic.go
  • 节点在线与流量上报:internal/logic/server/pushOnlineUsersLogic.gointernal/logic/server/serverPushUserTrafficLogic.go

新版 Pro 的关键参考入口包括:

  • Simnet 管理端字段:api/admin/server/v1/server.proto
  • OmnXT 节点兼容接口:internal/server/http_compat_server.go
  • 公共订阅响应:api/public/subscribe/v1/subscribe.proto
  • 公共订阅映射:internal/service/public/subscribe/subscribe.go
  • UA/capability 过滤:internal/biz/public/subscribe/subscribe.go
  • 节点交付数据:internal/data/delivery_node.go
  • 协议模型和默认值:internal/model/server/protocol.go

2. 目标与非目标

目标

  1. 在旧后端中完整支持 simnet 协议的保存、查询、下发、订阅和统计。
  2. 继续使用旧系统 Server 的 protocols JSON 保存协议配置,不强制拆表保存管理端协议配置。
  3. 第一版支持当前实际需要的能力:H2、TLS/SNI、AF、HTTPS Fallback。
  4. Reverse 字段先纳入模型和接口,默认关闭;不在第一版强制上线 Reverse 转发能力。
  5. 管理端配置、OmnXT 服务端运行配置、SlagClient 客户端订阅配置使用不同 DTO,避免敏感字段误下发。
  6. 使用 type + port 唯一定位一个 Server 内的协议实例,支持同一 Server 未来存在多个协议。
  7. OmnXT 拉取配置必须校验 secret_key
  8. Server 级 PSK 不得下发给普通用户。
  9. 优先设计每用户独立 Simnet Key ID/PSK,使用户隔离、封禁、重置和审计可控。
  10. SlagClient 订阅响应兼容 protocols JSON 和顶层 simnet_* 字段。

非目标

  1. 不整体替换旧后端为 Pro 新后端。
  2. 不一次性迁移 Pro 的全部协议字段、路由系统、完整 delivery node 架构。
  3. 不第一版实现 OmniFlow 或其他新协议。
  4. 不改变现有套餐、订单、余额、邀请等业务主链路。
  5. 不把生产服务器凭据、JWT、节点 SSH 密码写入代码或文档。

3. 总体技术策略

最科学的迁移方式是“协议纵向切入”,而不是“代码横向搬运”。也就是沿着 simnet 从管理端保存到节点运行,再到用户订阅、授权、流量统计的完整链路逐层补齐。

建议分三段落地:

  1. Server 侧先闭环:管理端能保存 simnetOmnXT 能用 secret_key 拉到运行配置。
  2. User 侧再闭环:每个用户生成独立凭据,OmnXT 用户列表和 SlagClient 订阅使用同一套凭据。
  3. 运维侧最后闭环:流量、在线、到期、限额、TLS/AF/Fallback、灰度和回滚全部验证。

核心原则:

  • 旧架构优先:沿用 goctl API、internal/types、现有 logic/model 风格。
  • DTO 分层:管理端 DTO 可以看到完整配置;节点 DTO 只给 OmnXT 运行需要;订阅 DTO 只给用户连接需要。
  • 敏感字段隔离:Server PSK、证书 DNS 环境变量、节点密钥不得进入普通用户订阅响应。
  • 渐进兼容:老协议、老客户端、老节点不受影响。
  • 可回滚:每个阶段都能通过关闭 simnet 协议或恢复旧接口行为回滚。

4. Simnet 数据链路

完整链路如下:

Admin UI
  -> POST /api/v1/admin/server/create or update
  -> Server.protocols JSON contains type=simnet

OmnXT Node
  -> GET /api/v1/server/config?server_id=...&protocol=simnet&secret_key=...
  -> receives server runtime config, including server-side PSK and TLS/AF/Fallback settings

OmnXT Node
  -> GET /api/v1/server/user/list?server_id=...&protocol=simnet&secret_key=...
  -> receives active user authorization list and per-user simnet credentials

SlagClient
  -> GET /api/v1/public/subscribe?token=... with capability headers
  -> receives node address, port, TLS/SNI, path, AF/Fallback public fields and user credential

OmnXT Node
  -> POST traffic / online user report
  -> backend maps simnet user credential to user subscribe and records traffic

simnet 的运行配置不能只靠 server.protocols 原样下发,因为同一份 JSON 同时包含管理端字段、Server 密钥字段和用户连接字段。必须在每个出口做字段筛选和转换。

5. 阶段 0:建立基线与确认契约

目标

确认旧后端、OmnXT、SlagClient 对 simnet 的最小契约,先把边界钉牢,避免后续实现时字段名、鉴权方式或客户端解析格式反复改。

具体任务

  1. 从新版 Pro 提取 simnet 管理字段、服务端字段、订阅字段的差异表。
  2. 用当前 OmnXT 安装脚本部署的版本抓取真实请求路径和请求参数。
  3. 用 SlagClient 抓取订阅请求 header,确认 capability header 名称和版本值。
  4. 确认 secret_key 当前在旧仓库 internal/middleware/serverMiddleware.go 或节点接口 handler 中的校验方式。
  5. 确认 server_id + protocol 是否已经足够定位节点运行配置;如果端口也会重复,需要补充 port 查询参数。

预计修改位置

本阶段原则上不改业务代码,只新增测试夹具或临时验证脚本。可新增:

  • tests/simnet/fixtures/
  • docs/simnet-contract.md,如需要更细的契约文档

依赖关系

  • 需要可运行的旧后端本地环境或测试库。
  • 需要 OmnXT 当前版本真实请求样本。
  • 需要 SlagClient 当前版本订阅响应解析规则。

验收条件

  1. 明确 OmnXT 配置接口路径、方法、请求参数和响应字段。
  2. 明确 SlagClient 识别 simnet 的字段格式。
  3. 明确 capability header 优先级:先 capability header,再 User-Agent 兜底。
  4. 明确 type + port 是协议实例唯一键。

回滚点

本阶段不涉及生产行为,无需业务回滚。

6. 阶段 1:协议模型与参数校验

目标

让旧后端的 Protocol 类型可以完整表达第一版 simnet 配置,并在创建/更新 Server 时有默认值和校验。

具体任务

  1. apis/types.apiProtocol 结构加入 simnet 字段。
  2. 重新生成 internal/types/types.go
  3. internal/model/node 中的协议模型加入同名 JSON 字段,保证 Server 的 protocols JSON 能完整 marshal/unmarshal。
  4. 新增 simnet 默认值函数,例如 ApplySimnetDefaults
  5. 新增 simnet 参数校验函数,例如 ValidateSimnetProtocol
  6. 校验 type + port 唯一,避免同一 Server 下出现两个 simnet:443
  7. 限制第一版允许值:simnet_carrier=h2security=tls|none,生产建议默认 tls
  8. 校验 path 必须以 / 开头,fallback host 非空时端口必须在 1-65535。
  9. 校验 simnet_psk 最小长度和字符集;自动生成时使用安全随机。

字段范围

核心字段:

simnet_psk
simnet_key_id
simnet_ticket_id
simnet_path
simnet_carrier

TLS 字段:

security
sni
allow_insecure
cert_mode
cert_dns_provider
cert_dns_env

AF 字段:

simnet_af_enabled
simnet_af_path_mode
simnet_af_path_prefix
simnet_af_path_suffix
simnet_af_magic_mode
simnet_af_response_jitter_ms
simnet_af_handshake_polymorphism
simnet_af_settings_jitter
simnet_af_fake_header_injection

Fallback 字段:

simnet_fallback_enabled
simnet_fallback_target_scheme
simnet_fallback_target_host
simnet_fallback_target_port
simnet_fallback_host_header
simnet_fallback_tls_sni

Reverse 字段:

simnet_reverse_enabled
simnet_reverse_listen_addr
simnet_reverse_listen_port
simnet_reverse_target_host
simnet_reverse_target_port

默认值

建议默认值如下:

port: 443
simnet_path: /simnet/session
simnet_carrier: h2
security: tls
allow_insecure: false
simnet_af_path_mode: api
simnet_af_magic_mode: derived
simnet_af_response_jitter_ms: 1
simnet_reverse_enabled: false
simnet_reverse_listen_addr: 127.0.0.1
simnet_fallback_enabled: true
simnet_fallback_target_scheme: https
simnet_fallback_target_port: 443

预计修改位置

  • apis/types.api
  • internal/types/types.go
  • internal/model/node/* 或实际定义 node.Protocol 的文件
  • internal/logic/admin/server/createServerLogic.go
  • internal/logic/admin/server/updateServerLogic.go
  • 可新增 internal/logic/admin/server/protocol_simnet.go

依赖关系

  • 阶段 0 的字段契约。
  • goctl 代码生成命令可用。

验收条件

  1. 管理端提交 type=simnet 时,Server 可以保存完整 JSON。
  2. 未传默认字段时自动补齐默认值。
  3. 非法 path、非法 port、重复 type + port 会被拒绝。
  4. 旧协议保存和返回不变。

回滚点

关闭管理端提交 simnet 的入口校验;或恢复 apis/types.api 和生成类型,旧协议数据仍可继续工作。

7. 阶段 2:管理端 Server 接口

目标

让管理端 Server 创建、更新、查询能完整展示和编辑 simnet,并保持 Node 更新接口与 Server 协议配置一致。

具体任务

  1. 更新 CreateServerRequestUpdateServerRequestFilterServerListResponseGetServerProtocolsResponse 中的协议字段。
  2. 在 create/update Server 时对每个 protocol 先做 normalize,再落库。
  3. 在 filter/list/detail 接口中返回规范化后的 simnet 字段。
  4. 检查 CreateNodeRequestUpdateNodeRequest 是否允许 protocol=simnet
  5. Node 端 node_type=front 的创建/更新要允许 simnet,并校验其 port 与 Server 里的 simnet 协议端口一致。
  6. 如果管理端前端需要协议选项,GetServerProtocols 要返回 simnet,并带默认字段方便 UI 填充。

预计修改位置

  • apis/admin/server.api
  • internal/types/types.go
  • internal/logic/admin/server/createServerLogic.go
  • internal/logic/admin/server/updateServerLogic.go
  • internal/logic/admin/server/filterServerListLogic.go
  • internal/logic/admin/server/getServerProtocolsLogic.go
  • internal/logic/admin/server/createNodeLogic.go
  • internal/logic/admin/server/updateNodeLogic.go

依赖关系

  • 阶段 1 协议模型已经可表达 simnet

验收条件

  1. 管理端能创建一个 Server,包含 simnet:443
  2. 管理端能更新 simnet_pathsni、AF 和 fallback 字段。
  3. 管理端节点列表显示 HK simnet 这类节点时,协议类型不丢失。
  4. GetServerProtocols 返回的 protocols JSON 与数据库一致且字段完整。

回滚点

从管理端把 simnet 协议 disabled,保留数据但不对节点下发;或回滚 Server 相关 API 和 logic。

8. 阶段 3:OmnXT 服务端配置下发

目标

让 OmnXT 节点通过旧后端节点 API 拉到可运行的 simnet 服务端配置。

具体任务

  1. 检查 apis/node/node.apiGetServerConfigRequest 是否有 secret_keyserver_idprotocol
  2. GetServerConfigLogic 中加入 protocol=simnet 分支。
  3. 根据 server_id + protocol + port 找到启用的 simnet 协议配置。
  4. 验证 secret_key,失败时返回明确错误,并记录来源 IP 和 server_id。
  5. 构造 OmnXT 服务端运行 DTO,包含 Server 运行需要的 PSK、path、carrier、TLS、SNI、AF、fallback、reverse 默认关闭字段。
  6. 不把管理端专用字段、无关协议字段原样塞给 OmnXT。
  7. 缓存 key 要包含 server_id + protocol + port,避免同端口多协议污染缓存。
  8. OmnXT 配置变更后要能通过更新 Server 或清理缓存生效。

服务端 DTO 建议

{
  "protocol": "simnet",
  "port": 443,
  "listen": ":443",
  "simnet_psk": "server-side-secret",
  "simnet_path": "/simnet/session",
  "simnet_carrier": "h2",
  "security": "tls",
  "sni": "example.com",
  "allow_insecure": false,
  "simnet_af_enabled": true,
  "simnet_fallback_enabled": true
}

预计修改位置

  • apis/node/node.api
  • internal/types/types.go
  • internal/logic/server/getServerConfigLogic.go
  • internal/logic/server/constant.go
  • internal/middleware/serverMiddleware.go
  • 可新增 internal/logic/server/simnet_config.go

依赖关系

  • 阶段 1 和阶段 2。
  • OmnXT 实际接口字段确认完成。

验收条件

  1. secret_key 正确时,OmnXT 能拉到 simnet 服务端配置。
  2. secret_key 错误时,请求被拒绝。
  3. 修改管理端 simnet_path 后,OmnXT 重启或刷新能拿到新 path。
  4. Server PSK 只出现在 OmnXT 服务端配置中,不出现在普通用户订阅中。

回滚点

关闭 simnet.enable 或回滚 GetServerConfigLogicsimnet 分支;旧协议节点不受影响。

9. 阶段 4:用户级 Simnet 凭据

目标

为每个有效用户订阅生成独立 simnet 凭据,避免所有用户共享 Server PSK,支持单用户封禁、重置和流量归属。

具体任务

  1. 新增用户级凭据模型,建议按 user_subscribe_id + server_id + protocol + port 维度唯一。
  2. 字段建议包括:iduser_iduser_subscribe_idserver_idprotocolportkey_idpskticket_idenabledcreated_atupdated_atrotated_at
  3. 添加数据库 migration,并在初始化兼容逻辑中保证表存在。
  4. 用户第一次订阅或节点第一次拉用户列表时懒生成凭据。
  5. 支持管理员重置某个用户订阅 token 时同步重置 simnet 凭据,避免旧凭据继续可用。
  6. 凭据生成使用加密安全随机;key_id 可用递增 id 或稳定 hash,但必须避免全局冲突。
  7. 保留 ticket_id 字段,第一版可为空或由 OmnXT 需要时生成。

预计修改位置

  • internal/model/user/* 或新增 internal/model/simnet/*
  • initialize/migrate/*
  • initialize/schema_compat.go
  • internal/logic/public/subscribe/queryUserSubscribeNodeListLogic.go
  • internal/logic/server/getServerUserListLogic.go
  • 用户订阅 token 重置逻辑:internal/logic/admin/user/resetUserSubscribeTokenHandler.go 对应 logic

依赖关系

  • 阶段 0 确认 OmnXT 和 SlagClient 需要的用户凭据格式。
  • 阶段 1 的协议模型完成。

验收条件

  1. 同一用户同一节点多次订阅拿到稳定凭据。
  2. 不同用户拿到不同凭据。
  3. 重置用户订阅 token 后旧凭据失效,新凭据生效。
  4. 凭据表有唯一约束,重复生成不会产生两条有效凭据。

回滚点

可以停止向 OmnXT 下发 simnet 用户授权,并禁用 simnet 节点。数据库表可保留,不影响旧协议。

10. 阶段 5:OmnXT 用户授权同步

目标

让 OmnXT 拉取用户列表时获得 simnet 可认证用户,并且用户到期、限额、禁用、套餐节点组变化后同步生效。

具体任务

  1. GetServerUserListLogic 中加入 simnet 用户映射。
  2. 沿用旧系统的有效用户筛选条件:订阅有效、未到期、流量未超限、用户未禁用、节点组有权限。
  3. simnet 用户返回 user_idsubscribe_iduuidkey_idpskticket_id、限速字段。
  4. OmnXT 请求 protocol=simnet 时,只返回有 simnet 权限的用户。
  5. 缓存 key 加入 protocol + port,用户订阅变更、流量变更、节点组变更时能失效。
  6. hysteria2 等旧兼容映射不做破坏;normalizeServerUserListProtocol 仅新增 simnet 透传。

预计修改位置

  • apis/node/node.api
  • internal/types/types.go
  • internal/logic/server/getServerUserListLogic.go
  • internal/logic/server/constant.go
  • 用户订阅、节点组、流量相关 model/service
  • 可新增 internal/logic/server/simnet_user.go

依赖关系

  • 阶段 4 用户级凭据。
  • 现有用户有效性判断需要梳理清楚。

验收条件

  1. OmnXT 拉用户列表时能看到有效用户的 simnet 凭据。
  2. 用户到期、禁用或流量超限后,从 OmnXT 用户列表消失。
  3. 套餐节点组取消该节点后,从 OmnXT 用户列表消失。
  4. 老协议用户列表响应不变。

回滚点

保留凭据表,但关闭 GetServerUserListLogicsimnet 分支或禁用节点。

11. 阶段 6:公共订阅与 SlagClient

目标

让 SlagClient 冷启动、重启、重新订阅时都能拿到完整 simnet 节点,并正确构造连接。

具体任务

  1. apis/public/subscribe.apiUserSubscribeNodeInfo 加入用户连接需要的顶层 simnet_* 字段。
  2. 保留 protocols JSON,确保 SlagClient 旧解析路径仍可读取。
  3. QueryUserSubscribeNodeListLogic 中解析 Server 的 protocols JSON,并把匹配 node.protocol + node.portsimnet 配置映射到订阅响应。
  4. 订阅响应只下发用户级 simnet_key_id、用户级 simnet_psk、可公开 path/carrier/TLS/SNI/AF/Fallback 字段。
  5. 不下发 Server 级 simnet_psk、DNS provider env、管理端密钥字段。
  6. 新增 capability header 判断,例如 X-Client-Capabilities: simnet 或当前 SlagClient 实际 header。
  7. 如果没有 capability header,则使用 User-Agent 作为兼容兜底;不应单纯依赖 UA。
  8. 对不支持 simnet 的客户端隐藏 simnet 节点,避免客户端崩溃或展示不可用节点。
  9. 如果 SlagClient 同时支持 protocols JSON 和顶层字段,优先让顶层字段完整,protocols 作为兼容冗余。

订阅 DTO 建议

{
  "id": 1,
  "name": "HK simnet",
  "protocol": "simnet",
  "port": 443,
  "address": "node.example.com",
  "sni": "net.example.com",
  "simnet_key_id": 10001,
  "simnet_psk": "user-side-secret",
  "simnet_ticket_id": "",
  "simnet_path": "/simnet/session",
  "simnet_carrier": "h2",
  "security": "tls",
  "allow_insecure": false,
  "simnet_af_enabled": true,
  "simnet_af_path_mode": "api",
  "simnet_af_magic_mode": "derived",
  "simnet_fallback_enabled": true,
  "simnet_fallback_target_scheme": "https",
  "simnet_fallback_target_host": "www.example.com",
  "simnet_fallback_target_port": 443
}

预计修改位置

  • apis/public/subscribe.api
  • internal/types/types.go
  • internal/logic/public/subscribe/queryUserSubscribeNodeListLogic.go
  • internal/logic/common/subscriptionTrace.go,如有订阅 UA 或设备记录
  • 可新增 internal/logic/public/subscribe/simnet_mapper.go

依赖关系

  • 阶段 4 用户级凭据。
  • SlagClient capability header 契约确认。

验收条件

  1. SlagClient 冷启动订阅后能看到 simnet 节点。
  2. SlagClient 重启后仍能从订阅恢复连接配置。
  3. 不支持 simnet 的客户端订阅不返回 simnet 节点。
  4. 普通用户订阅响应不包含 Server 级 PSK。

回滚点

订阅侧隐藏 simnet 节点或关闭 capability 开关;旧协议订阅不受影响。

12. 阶段 7:流量和在线用户映射

目标

让 OmnXT 上报的 simnet 在线用户和流量能正确归属到用户订阅,并触发旧系统现有的限额、日志、后台统计。

具体任务

  1. 确认 OmnXT 上报用户标识是 uuidkey_iduser_id 还是其他字段。
  2. 如果 OmnXT 上报 key_id,后端通过用户级凭据表反查 user_subscribe_iduser_id
  3. 如果 OmnXT 上报 uuid,需要确认 uuidsimnet 凭据绑定关系,不允许跨用户伪造。
  4. serverPushUserTrafficLogic 中加入 simnet 标识解析。
  5. pushOnlineUsersLogic 中加入 simnet 在线用户映射。
  6. 更新后台节点在线数统计,确保 simnet:443 与其他协议隔离。
  7. 失败上报要记录协议、server_id、port、用户标识和错误原因,方便排查。

预计修改位置

  • apis/node/node.api
  • internal/types/types.go
  • internal/logic/server/serverPushUserTrafficLogic.go
  • internal/logic/server/pushOnlineUsersLogic.go
  • internal/model/traffic/*
  • internal/model/node/*
  • 凭据表 model

依赖关系

  • 阶段 4 用户级凭据。
  • OmnXT 上报格式确认。

验收条件

  1. simnet 连接产生流量后,用户已用流量增加。
  2. 节点后台能看到 simnet 在线人数。
  3. 用户超限后 OmnXT 用户列表不再包含该用户。
  4. 旧协议流量统计不受影响。

回滚点

禁用 simnet 流量上报分支或关闭 simnet 节点;旧协议统计不受影响。

13. 阶段 8TLS、AF 与 Fallback

目标

把当前实际部署需要的 TLS/SNI、AF 和 HTTPS Fallback 做到可配置、可验证、可运维。

具体任务

  1. TLS:支持 security=tlssniallow_insecure=false
  2. 证书模式:第一版支持 cert_mode=httpDNS provider 字段先保留,不在普通订阅下发。
  3. AF:支持 simnet_af_enabledpath_mode=apimagic_mode=derivedresponse_jitter_ms
  4. Fallback:支持 fallback scheme、host、port、host header、TLS SNI。
  5. Reverse:字段保存和下发给 OmnXT,但默认关闭;如果开启必须要求 target host/port 完整。
  6. 添加配置快照日志,OmnXT 拉取时打印非敏感字段,便于确认线上配置是否生效。
  7. 对真实节点做 443 端口监听、证书申请、fallback 站点访问验证。

预计修改位置

  • internal/logic/admin/server/protocol_simnet.go
  • internal/logic/server/simnet_config.go
  • internal/logic/public/subscribe/simnet_mapper.go
  • etc/ppanel.yaml,如需要新增全局开关
  • 节点部署文档或运维脚本,视 OmnXT 实际需求决定

依赖关系

  • 阶段 3 OmnXT 配置下发。
  • 节点服务器域名、证书、端口和 fallback 目标准备完成。

验收条件

  1. OmnXT 能在 443 启动 simnet H2 TLS。
  2. SNI 与证书匹配。
  3. AF 开启后 SlagClient 仍可连接。
  4. Fallback 目标在非协议请求时可访问。
  5. OmnXT 重启后配置仍然生效。

回滚点

关闭 AF 或 fallback;必要时把 simnet.enable=false,保留旧协议节点承载用户。

14. 阶段 9:自动化测试

目标

用测试保护 simnet 的关键契约,减少后续修改协议字段时再次出现“面板有配置、节点拿不到、客户端不识别”的问题。

具体任务

  1. 协议模型测试:默认值、校验、marshal/unmarshal。
  2. 管理端测试:create/update Server 保存 simnet 字段完整。
  3. 节点配置测试:secret_key 正确/错误、simnet DTO 字段筛选。
  4. 用户凭据测试:生成稳定性、用户隔离、重置失效。
  5. 订阅测试:capability header 支持时返回 simnet;不支持时隐藏。
  6. 敏感字段测试:普通订阅中不得出现 Server PSK、DNS env。
  7. 流量测试:OmnXT 上报 key_id 后可归属用户。
  8. 回归测试:现有 vless、trojan、hysteria2、shadowsocks 订阅不变。

预计修改位置

  • tests/acceptance/*
  • internal/logic/admin/server/*_test.go
  • internal/logic/server/*_test.go
  • internal/logic/public/subscribe/*_test.go
  • internal/model/simnet/*_test.go

依赖关系

  • 阶段 1 到阶段 7 基本实现完成。

验收条件

  1. go test ./... 通过,或项目当前可执行测试集全部通过。
  2. 新增测试能覆盖 Server、OmnXT、SlagClient、Traffic 四条主链路。
  3. 任意敏感字段泄露测试失败时,CI 阻断。

回滚点

测试本身不影响生产;如果某阶段实现回滚,相应测试应标记待实现或一并回滚。

15. 阶段 10:灰度发布与回滚

目标

simnet 以可控方式上线,先让一个节点和少量测试用户跑通,再扩大范围。

具体任务

  1. 增加全局或配置级开关:simnet_enabled
  2. 管理端先创建一个独立测试 Server 和一个 simnet front node。
  3. 只给测试套餐或测试节点组分配该节点。
  4. 部署 OmnXT,确认能拉配置、拉用户、启动监听。
  5. 用测试用户订阅 SlagClient,验证冷启动、重启、切换网络、重拉订阅。
  6. 观察在线用户、流量上报、错误日志、证书续期和 fallback 访问。
  7. 稳定后把节点加入正式套餐节点组。
  8. 保留旧协议节点作为回退路径,不把全部用户一次性切到 simnet

预计修改位置

  • etc/ppanel.yaml,如需要全局开关
  • internal/config/config.go
  • internal/svc/serviceContext.go
  • 运维部署文档

依赖关系

  • 阶段 1 到阶段 9 完成。
  • 测试节点服务器、域名、证书、OmnXT 可用。

验收条件

  1. 测试用户能稳定连接 simnet
  2. SlagClient 重启后无需人工操作即可恢复。
  3. OmnXT 重启后能自动拉配置和用户授权。
  4. 管理端能看到在线和流量。
  5. 关闭 simnet 后用户可回退到旧协议节点。

回滚点

  1. 管理端将 simnet 协议 enable=false
  2. 从套餐节点组移除 simnet 节点。
  3. OmnXT 停止 simnet inbound。
  4. 回滚后端到上一版本。
  5. 保留凭据表和字段,后续排查后可再次启用。

16. 文件改动范围

预计完整生产可用版本会影响 27-45 个业务/配置文件、12-20 个测试文件,新增约 3,000-6,000 行代码和测试。实际数量取决于 goctl 生成文件体积、现有 model 组织方式和 OmnXT/SlagClient 契约是否稳定。

必改范围

  • apis/types.api
  • apis/admin/server.api
  • apis/node/node.api
  • apis/public/subscribe.api
  • internal/types/types.go
  • internal/model/node/*
  • internal/logic/admin/server/createServerLogic.go
  • internal/logic/admin/server/updateServerLogic.go
  • internal/logic/admin/server/getServerProtocolsLogic.go
  • internal/logic/admin/server/filterServerListLogic.go
  • internal/logic/server/getServerConfigLogic.go
  • internal/logic/server/getServerUserListLogic.go
  • internal/logic/server/serverPushUserTrafficLogic.go
  • internal/logic/server/pushOnlineUsersLogic.go
  • internal/logic/public/subscribe/queryUserSubscribeNodeListLogic.go

可能新增范围

  • internal/model/simnet/*
  • internal/logic/admin/server/protocol_simnet.go
  • internal/logic/server/simnet_config.go
  • internal/logic/server/simnet_user.go
  • internal/logic/public/subscribe/simnet_mapper.go
  • initialize/migrate/*simnet*
  • tests/simnet/*
  • docs/simnet-contract.md

前端联动范围

如果管理端前端也要同步配置,需要在前端仓库补齐:

  • Server 创建/编辑表单的 simnet 协议字段
  • 协议默认值填充
  • 字段校验提示
  • Node 创建/更新时允许 protocol=simnet
  • 隐藏 Server PSK 的展示或复制入口

17. 提交拆分

建议按以下提交拆分,方便 review 和回滚:

  1. simnet: add protocol model fields and validation
  2. simnet: support admin server create/update/list
  3. simnet: expose server runtime config for OmnXT
  4. simnet: add per-user credentials
  5. simnet: sync OmnXT user authorization
  6. simnet: expose public subscribe fields for SlagClient
  7. simnet: map traffic and online reports
  8. simnet: add tls af fallback handling
  9. simnet: add tests and rollout switch

每个提交都应该能单独说明行为变化,并尽量避免把 goctl 生成文件和手写逻辑混在一个巨大提交里。如果生成文件不可避免较大,提交说明中要明确哪些是生成结果。

18. 验收标准

最终验收必须覆盖下面场景:

  1. 管理端能创建 Server,协议为 simnet,端口 443TLS/SNI、AF、Fallback 字段保存完整。
  2. 管理端能创建或更新 Nodeprotocol=simnetaddress 指向实际节点服务器。
  3. OmnXT 使用正确 secret_key 能拉取 simnet 服务端运行配置。
  4. OmnXT 使用错误 secret_key 被拒绝。
  5. OmnXT 重启后自动恢复 simnet inbound。
  6. 有效用户能通过 OmnXT 用户列表获得授权。
  7. 不同用户的 simnet_key_idsimnet_psk 不相同。
  8. 用户禁用、到期或流量超限后,OmnXT 用户列表移除该用户。
  9. SlagClient 冷启动能通过订阅拿到 simnet 节点并连接。
  10. SlagClient 重启后不丢失协议配置。
  11. 不支持 simnet 的客户端订阅不会收到 simnet 节点。
  12. 普通用户订阅响应不泄露 Server PSK、DNS provider env、节点 secret_key
  13. simnet 连接产生流量后,用户流量、节点流量、后台日志同步更新。
  14. 关闭 simnet 后,旧协议订阅、节点运行和流量统计不受影响。
  15. go test ./... 或项目当前有效测试集通过。

19. 风险清单

风险 影响 控制方式
Server PSK 被下发给普通用户 所有用户共享密钥,泄露后整节点风险扩大 DTO 分层,订阅敏感字段测试阻断
OmnXT 和后端字段名不一致 节点启动失败或配置不生效 阶段 0 固化契约,用真实 OmnXT 请求回放测试
SlagClient 只读顶层字段或只读 protocols JSON 客户端拿到节点但无法连接 双格式兼容,顶层字段和 protocols 都保持可读
单用户凭据缺失 无法隔离用户,封禁和流量归属困难 阶段 4 必须先做凭据表,不走全员共享 PSK
capability 判断不准确 老客户端看到不可用节点 capability header 优先,UA 只兜底,默认隐藏不支持客户端
缓存 key 未包含 port 多协议或同协议多端口串配置 cache key 包含 server_id + protocol + port
流量上报标识不明确 用户流量无法入账或串账 与 OmnXT 明确上报 key_id,后端反查凭据表
TLS/证书/fallback 运维失败 节点 443 无法正常服务 灰度节点先跑,保留旧协议回退
goctl 生成覆盖手写改动 代码冲突或字段丢失 所有类型先改 api 文件,再生成;手写扩展放独立文件

20. 工期估算

在 OmnXT 和 SlagClient 契约清楚、测试环境可用的情况下:

  • 阶段 00.5-1 天
  • 阶段 1-21.5-2 天
  • 阶段 31-1.5 天
  • 阶段 41.5-2 天
  • 阶段 51-1.5 天
  • 阶段 61-1.5 天
  • 阶段 71-2 天
  • 阶段 81 天
  • 阶段 92-3 天
  • 阶段 101 天

完整生产可用版本预计 10-15 个有效开发日。如果 OmnXT 或 SlagClient 字段契约需要同步改动,额外预留 2-4 天联调时间。

21. 推荐执行顺序

第一周先完成最小闭环:

  1. 阶段 0:确认契约。
  2. 阶段 1:协议模型与校验。
  3. 阶段 2:管理端保存和查询。
  4. 阶段 3OmnXT 配置下发。

第二周完成用户链路:

  1. 阶段 4:用户级凭据。
  2. 阶段 5OmnXT 用户授权。
  3. 阶段 6SlagClient 订阅。
  4. 阶段 7:流量和在线用户映射。

最后做生产化:

  1. 阶段 8TLS、AF、Fallback 运维验证。
  2. 阶段 9:自动化测试补齐。
  3. 阶段 10:灰度发布和回滚演练。

22. 当前结论

最合理的方案是在旧后端内部补齐 simnet 的纵向链路,不建议整体迁移 Pro 新后端。这样风险最小,旧业务稳定性最好,也最贴近当前问题:SlagClient 和 OmnXT 需要的是一个一致、完整、不会泄露敏感字段的 simnet 契约。

第一版真正必须做的是:协议模型、管理端保存、OmnXT 配置、用户级凭据、OmnXT 授权、SlagClient 订阅、流量归属。只要这七个点闭环,simnet 就不是“配置看起来存在”,而是能在真实客户端和真实节点上稳定使用。