Files
aaaamoooo 3951134940
CI / test (push) Canceled after 0s
CI / anytls-smoke (push) Canceled after 0s
feat: Go port of the panel in panel-go/ — drop-in replacement for server.js
Pure-stdlib Go implementation of the whole control plane: same
nexusgate.json schema (unknown fields preserved verbatim, explicit
null/false tri-states modeled), same HTTP API and error strings,
embedded frontend (NG_PUBLIC_DIR override), self-implemented scrypt
auth, X25519 Reality keys, panel-hosted keyStore and heartbeat
reconciliation included. Version stays 0.6.8.

Verified: Go tests 33/33 (scrypt + Reality vectors generated from
the Node implementation, subscription goldens byte-identical),
Node suite 52/52 untouched, differential E2E against the Node
panel 50/50 including two-way DB swap. See panel-go/PARITY.md
and panel-go/INSTALL-GO.md.
2026-10-20 08:21:54 +00:00

77 lines
5.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# NexusGate 面板 Go 移植 — 行为对齐说明(PARITY)
Go 版(本目录)是 Node 版(../server.js + ../lib/)的 1:1 移植:同一
`nexusgate.json`、同一 HTTP API、同一前端(public/ 零改动,embed 进二进制)。
版本号保持 `0.6.8`(agent 的 AnyTLS 版本门禁按数字解析面板下发的版本串,
且 overview/healthz 的 version 字段与 Node 一致)。
## 已验证等价(2026-10-20,沙箱内)
- **Go 单元测试 33/33**:含 scrypt 测试向量 6 组(由 Node 实现生成,
`testdata/scrypt_vectors.json`,逐字节一致)、Reality 密钥向量 4 组
(固定私钥→公钥推导与 Node JWK 导出一致)、资源构建黄金深比较、
订阅 8 种格式黄金**逐字节**一致、store 事务/原子写/未知字段透传。
- **Node 测试 52/52**:未改动任何 Node 文件,原样全绿。
- **差分 E2E 正式模式 50/50**(`difftest/run.mjs`):同一种子 DB(由 Node
面板自身业务流生成)分别起 Node/Go 面板,跑登录/CSRF、建服务器+注册
令牌、agent enroll→带 resources 心跳→对账重排→poll→complete、建节点+
客户+分配→订阅全格式逐字比对、用量上报、节点删除/服务器遗忘、备份
比对、错误用例;随后**交换 DB 文件**互启对方实现,列表/订阅/写入均
正常,keyStore/jobs/用量不丢。
- **三态字段已精确建模**(差分首轮抓到并修复):`nodes.autoRemoving`
(缺失 vs 显式 false)、`servers.engine`(缺失 vs 显式 null)、
`customers.suspendReason`(缺失 vs 显式 null)在 Go 版用
指针/presence 标志区分,加载、响应、写回均与 Node 一致。
- 未知字段(其他版本写入的键)在记录级与顶层均透传保留,Node 写的 DB
由 Go 加载再写回不丢字段(store 测试 + 互通步骤双重覆盖)。
## 已知微偏差(均为边缘路径,不影响线上业务流)
1. **Cookie 解析**:值含畸形 `%` 转义时 JS `decodeURIComponent` 抛错
(请求 500);Go 跳过该对。Go 的 PathUnescape 也不拒绝非法 UTF-8
字节序列(JS 会抛错)。
2. **VerifySecret 的 hex 解码**:JS `Buffer.from(hex,'hex')` 对非法
hex 是截断容忍,Go 严格解码失败即 false;最终判定结果一致。
3. **字符串截断口径**:CleanText、错误截断(1800/240/80)、access 的
128/200 上限,Go 按 rune、JS 按 UTF-16 码元;仅星平面字符(emoji
等)恰在边界时可能差 1 字符。新密码 10 位长度校验同此口径。
4. **settings 缺失判定**:Go 用值结构体,以「五字段全零且无 Extra」
近似 JS 的 `!data.settings`;真实写路径不会产生全零 settings,
不可达。
5. **订阅信息页日期**:Go 按存储值(UTC)以 `2006/01/02 15:04:05`
格式化;JS `toLocaleString('zh-CN')` 按服务器本地时区渲染且随
ICU 版本可能有差异(旧 ICU 带上午/下午标记)。百分比 `%.1f` 与
JS `toFixed(1)` 在 .x5 边界可能差 0.1。
6. **NG_DATA_FILE 默认值**:Go 取可执行文件同目录 `data/nexusgate.json`,
JS 取源码目录 `__dirname/data`;正式部署都显式设置该变量,无影响。
NG_PORT 不可解析时 Go 回退 8787,JS 为 NaN 监听失败。
7. **ReadJSON 边缘**:body 为 `null` 时 Go 视为空对象,JS 返回 null
后在个别 handler 会 500;非对象 JSON 体 Go 返回 400,JS 各 handler
自行退化。路由参数解码失败时 Go 保留原始段,JS 抛错 500。
8. **JSString 近似**:对象/数组入参 Go 返回 "",JS 为
`"[object Object]"`/逗号拼接;现有 handler 不可达。
OptionalISO 只接受 ParseISO 覆盖的格式,JS `Date` 还接受
`2026/10/20` 等写法(Go 返回 400)。
9. **DNS 失败文案**:resolveBindAddress 报错括号内 Go 用解析器简述
(如 `no such host`),JS 用 getaddrinfo 的 code(如 ENOTFOUND);
仅 DNS 解析失败路径的文案细节不同。
10. **keyMaterial 回填遍历顺序**:多 keyId 时 Go map 迭代序与 JS
对象序不同,audit 先后可能不同;各 key 相互独立,结果等价。
11. **JSON 键序**:Go 响应/落盘的键序为结构体字段序(合并未知字段
时为字母序),JS 为插入序;JSON 语义等价,双方互读无碍(互通
步骤已验证)。
12. **HTTP 超时**:Go ReadHeaderTimeout 15s + ReadTimeout 30s 近似
Node 的 headersTimeout/requestTimeout 语义。
13. **TLS 域名校验**:RE2 不支持 lookahead,总长度 253 改在代码中
校验,接受/拒绝结果一致。
14. **畸形请求建记录**:节点创建缺 name 时 Go 存 "",JS 该键缺失;
服务器 labels 缺省 Go 为 `[]`,JS 为 undefined;仅手工畸形请求
可见,前端正常请求不触发。
## 构建与产物
- `build.sh`:从 ../public 刷新 embed_public 后 CGO_ENABLED=0 构建。
- 交叉编译(发布):`CGO_ENABLED=0 GOOS=linux GOARCH=amd64|arm64 go build -trimpath -ldflags="-s -w"`,
产物 `nexusgate-panel-linux-{amd64,arm64}`,纯静态、无外部依赖。
- 运行时可用 `NG_PUBLIC_DIR` 指定外部静态目录覆盖 embed(调试用)。