wangjia
cebc9a1c4f
fix(client/macos+server): 隧道运行时打通——DNS 劫持 + 死锁/空指针修复
...
接续 447f3f4(系统扩展可加载),修复"扩展能起但连上无法上网"的运行时问题,
现已在 macOS 15(cara)端到端连通:出口=节点 IP、国外站可达、DNS 经隧道解析。
服务端(clientconfig.go):
- route.rules 首条加 {"action":"hijack-dns","port":[53]}(排在 LAN 直连规则前)。
否则发往隧道 DNS(172.19.0.2:53)的查询被 172.16.0.0/12 吞去直连,域名解析失败。
sing-box 1.13 按端口劫持(protocol:dns 需先 sniff,不稳)。
客户端——保留三个真 bug 修复:
- startTunnel 的 libbox 启动移到后台队列:避免在 provider 队列同步阻塞,与
openTun→setTunnelNetworkSettings 回调三方死锁(隧道永远卡 connecting)。
- startOrReloadService(options:) 传非空 LibboxOverrideOptions():传 nil 致空指针 SIGSEGV。
- startDefaultInterfaceMonitor 阻塞到首个 path 更新再返回:修 "no available network interface"。
- 清除排障期临时诊断代码;CFBundleVersion 递增(sysextd 按版本去重)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
Claude-Session: https://claude.ai/code/session_01JEHzjEcFzvGwgbxT6Wbt6c
2026-06-22 08:57:55 +08:00
wangjia
43b25c8aa0
feat: 国内流量直连分流(geoip-cn / geosite-cn)— #5
...
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
国内 IP/域名直连(不走隧道)→ 省流量 + 国内访问快;非国内走代理。
- clientconfig.go: BuildClientConfig 加 ClientConfigOpts{SplitCN,RulesBaseURL};
开启时 route 加 {rule_set:[geoip-cn,geosite-cn]→direct} + 定义 rule_set
(remote .srs,download_detour:direct 直连下载)
- rules.go: 控制面静态服务 /v1/rules/{name}.srs(白名单防穿越)——自托管避免
GitHub 在国内被墙的鸡生蛋;客户端反正连控制面,可达性有保证
- nodes.go ConnectNode: 读 ?split_cn → opts;NodeAPI 加 rulesBaseURL
(PANGOLIN_PUBLIC_URL);main.go 挂 /v1/rules 路由 + RulesHandler
- 客户端: connect_api splitCN→?split_cn=1;connection_provider 传 smartRoute 偏好
- deploy/single-node: 拉 geoip-cn/geosite-cn.srs 到 $DATA_DIR/rules +
设 PANGOLIN_PUBLIC_URL/PANGOLIN_RULES_DIR
验证:go test(splitCN 开/关渲染 + RulesHandler 白名单/404)+ flutter analyze +
shellcheck;不需要节点。订阅链接暂用默认 opts、DNS 分流为后续增强。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-19 20:34:00 +08:00
wangjia
da8f3a4fef
feat(server): Hy2 自签 TLS 证书(方案①)— 补全 hysteria2 入站可用
...
Hy2 跑在 QUIC 上、强制 TLS,需服务端证书(不像 REALITY 借大站证书)。此前
handler_grpc 只发 ListenPort、cert 路径为空 → hysteria2 入站起不来。改为节点自签:
- agentd/hy2cert.go: ensureSelfSignedCert 生成 ECDSA P-256 自签证书到
/etc/sing-box/hy2.{crt,key}(幂等,缺失才生成;key 0600;SAN=reality SNI)
- singbox.go: writeAndRestart 渲染前对启用 hy2 的节点 ensure 证书并把
CertPath/KeyPath 写回 hy2 配置
- httpapi/clientconfig.go: hy2 出站 TLS 加 insecure:true + server_name,收自签
(两端自有,服务端鉴权靠 per-user 派生的 hy2 密码)
验证:单元测试(证书可被 crypto/tls 加载/幂等/0600)+ sing-box check 接受自签 hy2 入站。
注:节点实际启用 hy2 还需配 Hy2Port + 放行 UDP(单独步骤)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-19 12:12:35 +08:00
wangjia
93ec421c41
fix(plans): /v1/plans 500 修复 — plans 表无 name_zh/name_en 列
...
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
ListPlans 历来 SELECT name_zh/name_en,但 plans 表从未有这两列(各迁移/seed
均无)→ 该端点一直 500(无 live 测试故未暴露)。改为不查名称列(套餐名是 UI
文案,客户端 PlansScreen 按 code 取 l10n:free/pro/team)。价格等真实字段保留。
已在节点实测:/v1/plans 200,返回 free ¥0 / pro ¥25 / team ¥99。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-19 00:32:40 +08:00
wangjia
3d87bffbe9
feat(server): P2 后端补字段 — 套餐价格 + 节点 host( #6 6B)
...
- 迁移 000014_plan_pricing(mysql+sqlite 双套):plans 加 price_cents/currency/
period,seed pro=2500 team=9900(¥25/¥99);ListPlans 返回价格字段。
- /v1/nodes 暴露 host+port(取自 node.Endpoint,无 schema 变更),供客户端实测
真实 per-client 延迟(服务端无法代知)。
- 修复 account.go 的 GetMe/weeklyGB 残留 MySQL 专属 UTC_DATE()/INTERVAL
(#1 漏网):改 Go 端算日期传 ?,否则 SQLite 节点今日/周流量恒 0。
- sqlite 迁移 up/down 测试期望版本 13→14。
go build/vet 干净;sqlite 迁移 up/down + httpapi 测试通过。节点 tag 暂不加
(无真实数据,不造空字段;客户端将不显示伪造 tag)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-18 23:42:23 +08:00
wangjia
b9e5dbec9b
refactor(client): 抽统一日志函数 logLine + 去掉重复时间戳
...
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
- log_time.dart → log.dart:提供 logLine(tag, msg),格式 `[时:分:秒.毫秒] [tag] msg`,
DesktopKernel / DesktopVpnBridge 散落的 print+拼接全部收敛到此。
- 下发配置 log.timestamp=false:sing-box 不再自带时间戳,避免一行打印两个时间
(之前 `[22:10:35.213] [stderr] +0800 2026-06-18 22:10:35 ...` 重复)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-18 22:17:09 +08:00
wangjia
3a20a40101
fix(client-config): Hy2 未启用时不下发 hy2-out(消除 urltest 死成员探测)
...
hy2_port 为空(当前默认,Hy2 证书未接)时,旧逻辑仍把 hy2-out 默认指向
REALITY 的 TCP 443 端口塞进 urltest 探测组,导致客户端持续探测一个无 Hy2
监听的死成员 → "connection reset by peer"。改为 hy2Enabled 才下发 hy2-out
并加入探测组;否则 outbounds/urltest 只含 reality-out。已校验下发配置。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-18 22:11:00 +08:00
wangjia
52edb377e4
fix(client-config): 去掉 local DNS 的 detour:direct(sing-box 1.12 运行时 FATAL)
...
sing-box 1.12 start-service 阶段拒绝 DNS server detour 到空 direct 出站
(FATAL: detour to an empty direct outbound makes no sense)。local DNS 去掉
detour 后走默认路由,功能不变。已在节点用 `sing-box run`(空 inbound 复现
start-service)实测启动无 FATAL —— sing-box check 抓不到这类运行时错。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-18 21:51:10 +08:00
wangjia
bdf67cb56e
fix(client-config): 加 route.default_domain_resolver(sing-box 1.12+ 必需)
...
sing-box 1.13 对缺失 default_domain_resolver 直接 FATAL。加 {"server":"local"}
后,节点下发的整份客户端配置经 `sing-box check` 验证通过(无 FATAL/废弃)。
顺带 local_test.sh:免密已生效则跳过(不再反复要密码)+ --no-build 快速重跑。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-18 21:35:50 +08:00
wangjia
df72ad60f4
fix: DNS 用 sing-box 1.12+ 新格式 + 引导仅首次显示
...
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
- clientconfig/mockserver:客户端配置 DNS 从 legacy address 串格式改为
type+server 新格式(sing-box 1.13 对 legacy DNS 直接 FATAL,导致连不上)
- 引导页只首次:TokenStore 加 markOnboarded/isOnboarded;main.dart 登录后
若已引导过直接进主界面,不再每次都弹引导
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-18 21:20:11 +08:00
wangjia
ece51d7e3d
refactor(server/db): 多库(2/4)— 时间等 DB 端计算退回 Go(可移植)
...
auth/admin/httpapi:把 UTC_TIMESTAMP(6) 等 DB 端取值改为 Go time.Now().UTC()
作为 ? 参数传入(两库精度一致、可测、天然跨方言)。仅这三个文件不涉及
upsert/锁,故独立成提交;其余域文件的同类改动与 dialect 改动同语句交错,合入(3/4)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-18 00:01:12 +08:00
wangjia
8047d17b48
feat(server): web 用户中心后端(2/3) — 订阅 URL 端点
...
- GET /v1/me/subscription:返回 {url}=<SUB_BASE 或请求 host>/sub/<token>,
首次访问按需生成 users.sub_token。
- POST /v1/me/subscription/reset:轮换 sub_token,返回新 url。
- GET /sub/{token}(公开,无 JWT):按 token 查用户 dp_uuid,取首个活跃节点,
复用 httpapi.BuildClientConfig 渲染 sing-box 客户端配置返回(仅在配置了节点存储时挂载)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 08:38:03 +08:00
wangjia
009dbb8d07
feat(server): web 用户中心后端(1/3) — GetMe 补字段 + logout + redeem 路径别名 + 用户表 totp/sub_token
...
为 web 用户中心接通真后端做准备(保持 snake_case 不破 app):
- migration 000013:users 加 sub_token / totp_secret_enc / totp_enabled。
- GetMe 扩展:补 devices_used/devices_max/quota_today_min/data_today_gb/
weekly_gb/totp_enabled/expires_at(聚合 plans+usage_daily+devices+totp 列),
保留原 expire_at 等字段不破 app。
- POST /v1/auth/logout:X-Refresh-Token 头 → RevokeRefresh,幂等 204。
- /v1/me/redeem 别名(web 用),保留 /v1/redeem(app 用)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 08:35:09 +08:00
wangjia
cadd527680
feat(backend): 挂载 /v1 API + 实现 nodes/connect 端到端
...
- 新增 internal/dpcred 包,统一 DeriveHy2Password + DefaultFlow
agentd 与 HTTP connect handler 共享同一实现
- 新增迁移 000011:nodes 表拆分 reality_prk 私钥 / reality_pbk 公钥
reality_short_id;修正 handler_grpc.go 使用私钥字段
- 新增迁移 000012:connect_credentials 持久化凭证
实现 CredentialsForNode 修复 agent 重连 resync 原先返回空的桩
- 扩展 NodeStore 接口:ListUp / EntitlementForUser /
PersistCredential / DeleteCredential;同步 grpc_test.go mock
- 新增 httpapi/nodes.go:GET /nodes、POST /nodes/id/connect
Hub.Push + PersistCredential + 渲染完整 sing-box client 配置 JSON
POST /nodes/id/disconnect
- 新增 httpapi/account.go:GET /me、GET /plans、GET /notices
- 新增 httpapi/clientconfig.go:BuildClientConfig 服务端渲染
- 重写 cmd/server/main.go:手写 chi public/protected 分组
nodes.Service/Hub 在 main 构造并共享;SMTPMailer/LogMailer
go build ./... && go vet ./... && go test ./... 全部通过
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-15 23:09:56 +08:00
wangjia
dd060fd34a
feat(1d): wire oapi-codegen, generate types/server/client, add 501 stubs — tsk_Kp80nvHV2yLc
...
- Fix openapi.yaml: quote bearerAuth description that contained unquoted `: ` plain-scalar YAML
- Add server/api/cfg-{types,server,client}.yaml — oapi-codegen v2 config for three split outputs
- Add server/api/gen/gen.go — three //go:generate directives; run `make generate`
- Generate server/api/gen/{types,server,client}.gen.go from the contract (all 15 operations)
- Add github.com/oapi-codegen/runtime v1.4.1 to go.mod (runtime types needed by generated code)
- Add server/internal/httpapi/unimplemented.go — UnimplementedServer satisfies gen.ServerInterface;
every method writes HTTP 501 + {code,message_zh,message_en} JSON body (no dep on task-1f apierr)
- Update server/cmd/server/main.go — mount /v1 API via gen.HandlerFromMuxWithBaseURL; keep /healthz
- Update server/Makefile — `generate` target now runs `go generate ./...`;
add `check-generate` CI guard (regenerate + git diff --exit-code)
- Add server/internal/httpapi/unimplemented_test.go — 19 httptest cases covering all 15 API routes
(501+body), undefined routes (404), and /healthz isolation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-13 14:36:21 +08:00