Commit Graph

501 Commits

Author SHA1 Message Date
wangjia 3d521973ab feat(server): CORS 中间件(Web 用户中心跨域调 /v1/*)
Deploy Server / deploy-server (push) Successful in 2m56s
usercenter(app.yanmeiai.com,浏览器)跨域调控制面 API 会被浏览器拦(无 CORS 头)。
加白名单 CORS 中间件(CORS_ORIGINS,缺省 app.yanmeiai.com + pages.dev),应答 OPTIONS
预检。原生端非浏览器不受影响。Bearer 认证故不需 Allow-Credentials。含 3 项测试。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
server-v1.0.2
2026-07-06 21:52:33 +08:00
wangjia cb8e4bddd8 feat(web): 官网恢复登录入口 → usercenter(app.yanmeiai.com)
usercenter(web/usercenter, Next 静态导出)已部署到 CF Pages(pangolin-usercenter,
自定义域 app.yanmeiai.com)。site.ts 加 usercenter 字段;Header 恢复 .linklogin(t.login)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 21:48:59 +08:00
wangjia 4cfe4d0ccb chore(ci): deploy-client 本轮 Android-only(暂无 windows runner, 移除 build-windows)
Deploy Client / build-android (push) Successful in 2m9s
Deploy Client / release-deploy (push) Successful in 51s
release-deploy needs 改 [build-android];windows job/下载步骤 git 历史 2698116 可恢复。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
client-v1.0.48
2026-07-06 20:51:18 +08:00
wangjia 366695ffd6 chore(web): Windows 下载按钮暂改「即将推出」(其 CI 未产出 exe;本轮仅 Android 真下载)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 19:58:18 +08:00
wangjia 2e631ed3b5 feat(server+web): 客户端下载闭环——pangolin-server /downloads + 官网下载链接
Deploy Server / deploy-server (push) Successful in 2m59s
- server: /downloads/* 公开静态服务(NewDownloadsHandler,DOWNLOADS_DIR 默认
  /var/lib/pangolin/downloads),防目录穿越/不列目录/缺目录不崩,Cache-Control:
  no-cache 保证 CI 覆盖后永远最新。挂在 /healthz 同级(免鉴权)。含 4 项测试。
- deploy.sh: install -d 下载目录 + server.env 注入 DOWNLOADS_DIR。
- web: site.ts 增 downloads.{android,windows}(→ https://api.yanmeiai.com/downloads/*);
  Download.astro 接上 Android/Windows 按钮,iOS/macOS/Linux 改「即将推出」禁用态。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
server-v1.0.1
2026-07-06 19:48:57 +08:00
wangjia 2698116696 feat(ci): 客户端 CI 脚手架(Android+Windows, 照 jiu)——起草+Android 本地验证
- compile-android.sh: 建 libbox.aar + split-per-abi 取 arm64-v8a(~82MB, 避 232MB
  universal) + release 签名(RELEASE_KEYSTORE/KEY_PASSWORD)。本地已验证构建通过、
  libbox.so 打进 APK。
- compile-windows.sh: 复用 client/windows/installer/pangolin.iss(Inno Setup),不签名。
- release-client.sh / deploy-client.sh: 复用 pangolin lib-forgejo/lib-ssh,传 Gitea
  release + 部署最新到 pangolin1:/var/lib/pangolin/downloads(仅留最新)。
- deploy-client.yml: client-v* tag → build-android(mac)+build-windows(windows) → release-deploy(mac)。
- build.gradle: 补 release 签名 config(读 key.properties, 缺则回退 debug)。
- 待确认: keystore alias/密码(见脚本 TODO);runner 需提到 user 级;服务器 /downloads + 官网链接下一步。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 19:40:34 +08:00
wangjia 973c2c021b fix(ci): 网络操作加超时(runner 长任务 ~14min 无日志崩溃 → 快失败可诊断)
Deploy Server / deploy-server (push) Successful in 3m24s
Setup Go 下载 + Forgejo GET/create/upload 的 curl 原本无 --max-time,网络停顿即
无限挂起(疑似 runner 长任务崩溃且不留日志的根因)。加 --max-time(+GET/下载 retry),
让停顿在超时内快失败并留下日志,而非静默 14 分钟后崩。POST 不 retry(避免重复建/传)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
server-v1.0.0
2026-07-06 18:43:59 +08:00
wangjia f1950ffb6f fix(ci): release-server 首次实跑修复 + go 工具链 sha256 pin
Deploy Server / deploy-server (push) Failing after 13m45s
- lib-forgejo/release-server: 5 处 read -r X < file 在 set -e 下遇无结尾换行的
  EOF 会返回 1 静默中止(ver_from_tag printf 无换行、curl -w http_code 无换行);
  逐处加 || true 容错(值已赋)。
- lib-forgejo: 空数组 ${FORGEJO_CURL_TLS[@]} 在 set -u 下老 bash 报 unbound;
  改 empty-safe 展开(可移植 + 本地可测)。
- deploy-server.yml: Setup Go 加 sha256 校验(供应链完整性, 防镜像篡改注入工具链)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 18:04:51 +08:00
wangjia 8850c94e96 fix(ci): deploy-server 先装 go(runner 无 go → compile exit 127)
Deploy Server / deploy-server (push) Failing after 2m16s
catthehacker ubuntu:act-latest 自带 node 不带 go;直接 go build 报 command not found。
加 Setup Go 步骤从 golang.google.cn(CN 镜像, 避开墙内不稳的 go.dev)装 1.25.10。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 17:55:33 +08:00
wangjia 4d67a90659 fix(ci): deploy-server 隧道 healthcheck 改非致命(本地 healthz 才是权威闸)
Deploy Server / deploy-server (push) Failing after 22s
隧道 /healthz 依赖 cloudflared/CF 边缘,与本次二进制是否健康是两回事;
CF 边缘抖动或隧道尚未 provision 不应判整次部署失败。改为失败仅告警。
(最终 whole-branch review 的 Minor A 决议)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 16:59:52 +08:00
wangjia cccbb7b56b docs: 控制面 CF Tunnel/端口布局说明 + 方案 HTML 登记 index
CLAUDE.md 补充控制面经 Cloudflare Tunnel 对外的端口/URL 布局说明;生成
docs/control-plane-tls-tunnel.html 阅读版并登记 docs/index.html「实现计划」;
顺带修正 deploy/single-node/deploy.sh 摘要 echo 里残留的旧明文口描述(Task 4/5
落地后措辞漂移,实际已是 CF Tunnel + loopback)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 16:53:36 +08:00
wangjia 06ba76e22b feat(deploy): 8080 收 loopback + 关 8080 防火墙 + 健康检查改本地/隧道(退役明文控制口)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 16:47:32 +08:00
wangjia e2ffce90d3 feat(deploy): PANGOLIN_PUBLIC_URL 改 https://api.yanmeiai.com(客户端规则集走隧道)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 16:45:00 +08:00
wangjia 1b091f3be0 ci: 守护 Android manifest 禁全局明文(#25 控制面已 https)
新增 ci/scan-cleartext.sh 扫描 client/android AndroidManifest.xml,
防止将来把 usesCleartextTraffic="true" 重新加回(退回明文态)。
CI 新增 cleartext-scan job,并把该脚本纳入 lint job 的 shellcheck 覆盖
(单独挂载 ci/ 目录,不动 scripts/ci 既有挂载)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 16:41:59 +08:00
wangjia a5aea9438a feat(client): 控制面基址默认 https://api.yanmeiai.com + 移除 Android 明文开关
TDD: 先加守护测试(断言 kApiBaseUrl 走 https 且不含硬编码 IP),确认失败后
再改 api_config.dart 默认值 + 去掉 AndroidManifest 的 usesCleartextTraffic。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 16:37:42 +08:00
wangjia 9b56e2667b feat(deploy): cloudflared 出站隧道前置控制面 API(api.yanmeiai.com→127.0.0.1:8080)
deploy.sh 在 systemd 安装段新增 cloudflared 二进制(apt 源)+ unit 安装;
enable-on-boot 用 /etc/pangolin/cloudflared.env(TUNNEL_TOKEN)存在与否作
门槛,避免全新单机部署在 token 未落地前因 cloudflared 起不来而失败。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 16:32:32 +08:00
wangjia bac79017ae docs(plan): #25 控制面 CF Tunnel 实现方案
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 16:30:11 +08:00
wangjia 5c647235aa chore(todo): #25 方案 propose/approve + #32 fallback 登记
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 16:28:45 +08:00
wangjia f8e870fd6a fix(ci): backup 大小 sanity 阈值 50k→1k(小库压缩后 <50k 被误删)
远端 .backup 出的库 gzip 后可能 <50KB,原 >50k 检查把有效备份当'异常小'删了。
空 gzip 仅 ~20 字节,>1k 足以区分空/断流与有效备份。
2026-07-06 15:34:18 +08:00
wangjia 84b042394d ci(backup): 备份步骤输出 tee 到 NAS _last-run.log(便于排障) 2026-07-06 15:32:10 +08:00
wangjia e557a94ed0 fix(ci): lib-ssh 加 IdentitiesOnly+BatchMode(修 Too many auth failures)
CI ssh 到 pangolin1 报 Too many authentication failures + 回落密码提示:ssh 把
容器默认/agent key 也一并递上,撞 MaxAuthTries。加 IdentitiesOnly=yes 只用 -i key,
BatchMode=yes 纯非交互。影响 deploy-server / backup 等所有走 lib-ssh 的部署。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 15:05:44 +08:00
wangjia e1a17acbe4 feat(ci): SQLite 每日备份到 NAS(异地容灾,#26/F2)
backup-db.sh:ssh pangolin1 → sqlite3 online .backup 取 WAL 一致快照 + 远端
integrity_check → gzip 流回 → 存 NAS /volume1/docker/backups/pangolin,保留 30 天。
backup.yml:每日 02:00(北京)cron + 手动,ubuntu-latest,复用 DEPLOY_SSH_KEY,
Telegram 通知。异地=家里 NAS≠VPS 机房。不在 pangolin1 装/常驻任何东西。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 14:55:11 +08:00
wangjia 3efd67297e ci(server): 编译/测试直接在 runner 跑,去嵌套 docker(修 DinD;go 工具链自动下载)
compile-backend/test.sh 原在 golang:1.25 容器里跑,job 容器内 $PWD 在宿主不存在
→ DinD 挂载失败。改直接跑;go.mod 要求 1.25.10,靠 Go 工具链经 goproxy.cn 自动拉取。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 14:40:09 +08:00
wangjia f6e565ffdc ci(site): 官网构建/部署直接在 runner 跑,去掉嵌套 docker(修 DinD 挂载)
Deploy Site / deploy-site (push) Successful in 1m23s
job 容器内 $PWD(/workspace/...)在宿主不存在,docker run -v $PWD 挂载失败。
runner 镜像 catthehacker/ubuntu:act-latest 自带 node/npx,直接 bash 跑即可。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
site-v0.0.3
2026-07-06 14:34:12 +08:00
wangjia ba5f71b716 ci: runs-on nas → ubuntu-latest(匹配 nas runner 实际 label)
Deploy Site / deploy-site (push) Failing after 1m36s
nas 上的 forgejo-act-runner 注册 label 是 ubuntu-latest(非 nas),导致所有
runs-on: nas 的工作流无匹配 runner。改为 ubuntu-latest 对齐 runner 现状。
(备选:给 runner 加 nas 标签保约定,未采用。)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
site-v0.0.2
2026-07-06 13:33:27 +08:00
wangjia 7c26050cdb docs(spec): 官网部署改 Cloudflare Pages(架构变更说明)
Deploy Site / deploy-site (push) Has been cancelled
节点 :443 被 VPN 占用 + CF 免费套餐改回源端口需 Enterprise → 官网改 CF Pages 托管
(纯静态/全程 HTTPS/CSP 生效/无 :443 冲突),已上线 pangolin.yanmeiai.com。
deploy-site.sh 用 wrangler,需 CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
site-v0.0.1
2026-07-06 10:08:20 +08:00
wangjia 673914783d refactor(ci): 官网部署改用 Cloudflare Pages(替代 rsync 到 pangolin1)
节点 :443 被 sing-box(VPN 数据面)占用,CF 免费套餐回源只能打 :80/:443、改端口需
Enterprise —— 故官网改由 CF Pages 托管(项目 pangolin-site,自定义域 pangolin.yanmeiai.com),
纯静态、全程 HTTPS、_headers/CSP 自动生效、不落 VPS、彻底无 :443 冲突。
deploy-site.sh 改为 wrangler pages deploy(需 CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID
Gitea secret);deploy-site.yml deploy 步骤在 node:20 容器内跑 wrangler。lib-ssh 仍供服务端部署用。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 09:59:58 +08:00
wangjia 443d4e3a11 feat(ci): test.sh(go test / flutter test)
Add scripts/ci/test.sh <server|client>: server runs `go test ./...`
inside golang:1.25 (mirrors ci.yml's go-server job docker invocation +
gomod/gobuild caches + GOPROXY from _env.sh); client runs `flutter test`
inside ghcr.io/cirruslabs/flutter:stable (mirrors ci.yml's flutter-client
job's pub-cache mount + test dirs). Wired into deploy-server.yml's Test
step already added in Task 4. Registers the script in ci.yml's shellcheck
file list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 00:37:48 +08:00
wangjia 0825170044 fix(ci): 服务端流水线注入加固(tag 校验/JSON 转义/workflow env)+ gitignore server/out
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 00:33:47 +08:00
wangjia 4fb3fe3fee feat(ci): 服务端 server-v* 编译+release+部署(备份/迁移/回滚,复用 lib-ssh/lib-forgejo)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 00:24:18 +08:00
wangjia 91d1355721 fix(ci): 部署脚本 —— 私钥清理 trap 前移 + rsync 空目录守卫 + 主机密钥 accept-new(评审 Critical/Important)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 00:17:37 +08:00
wangjia 9bac5c8dcb feat(ci): 官网 site-v* 构建+部署脚本(compile-site/deploy-site/lib-ssh/workflow)
- scripts/ci/compile-site.sh: node:20 容器内构建 web/website(Astro),
  SITE_URL 注入 canonical 域名 https://pangolin.yanmeiai.com。
- scripts/ci/lib-ssh.sh: 新增共享 setup_ssh/teardown_ssh(写临时私钥+
  known_hosts),供 site 与后续 server 部署复用;目标写死 IP 103.119.13.48
  (runner 无法解析用户本机 ~/.ssh/config 的 pangolin1 别名)。
- scripts/ci/deploy-site.sh: rsync dist/ 到 pangolin1
  /var/www/pangolin-site/,root 部署。
- .gitea/workflows/deploy-site.yml: site-v* tag + workflow_dispatch 触发,
  concurrency 组 deploy-site 防并发。
- .gitea/workflows/ci.yml: shellcheck 列表纳入三个新脚本。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 00:10:53 +08:00
wangjia f0a8c6dd98 ci(checks): 触发路径纳入 scripts/ci(否则 CI 脚本改动不触发 lint)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 00:05:26 +08:00
wangjia 0b9f291ff1 ci(checks): shellcheck 覆盖 scripts/ci
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 00:03:05 +08:00
wangjia ff8fc36726 fix(ci): lib-forgejo TLS 校验默认开启,-k 改显式 opt-in(安全)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 23:55:56 +08:00
wangjia b8343a8788 feat(ci): scripts/ci 基座(_env/lib-forgejo/notify)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 23:49:59 +08:00
wangjia 6be777dff5 docs(plan): CI/CD 实现计划(#30) —— .md 真相源 + HTML 阅读版 + 索引
三期 11 任务:Phase1 基座+官网+服务端(无签名) → Phase2 Android → Phase3 macOS/Windows。
服务端部署固化 F3/F4 备份/迁移/回滚;下载链接接 release 资产;密钥对齐 jiu。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 23:40:59 +08:00
wangjia 0ab31b4c58 docs(spec): CI/CD 密钥命名对齐 jiu(MACOS_*/APPSTORE_*/ANDROID_*)
Apple 那套账户级与 jiu 共用一份(同名);新增 FORGEJO_URL、macOS 两个描述文件
入表。Android keystore 同命名规范但各 app 独立不共享。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 23:19:29 +08:00
wangjia fe9a3a6cdb docs: CI/CD 方案 HTML 阅读版 + 登记 docs/index.html(#30)
深色家族风 HTML(cicd-design.html),真相源仍是 superpowers/specs 的 .md。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 22:06:25 +08:00
wangjia f30097172a docs(spec): CI/CD 全流程设计方案(#30)
范围 A~F(官网+服务端+Android/macOS/Windows,排除 iOS/#26/#25);runner 混合
(nas/mac/windows);tag 触发;镜像 jiu 的 scripts/ci+workflows 结构;密钥作用域
(Apple/token 账户级、部署key/keystore 仓库级);服务端部署固化 F3/F4 手动次序带回滚。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 22:02:40 +08:00
wangjia 70d3293251 refactor(website): 域名/邮箱/渠道收口到 src/config/site.ts 单一真相源
新增 SITE 配置(url/email/telegram/line/store),Footer、Pricing、astro.config
(canonical 域名)均改为引用它;改联系方式/域名只动一处。
astro.config.mjs 直接 import 该 TS(Astro 配置加载支持)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 19:23:19 +08:00
wangjia a454c63c4e fix(website): 落定真实域名/邮箱/Telegram
- canonical 域名 → https://pangolin.yanmeiai.com(astro.config SITE_URL 默认值)
- 联系邮箱 support@/buy@pangolin.vpnpangolin@yanmeiai.com(可点 mailto)
- Telegram @PangolinVPN_bot → @pangolin_app(可点 https://t.me/pangolin_app)
邮件转发已在 CF 配好:pangolin@yanmeiai.comchenxin880812@gmail.com(目标已验证)。
未动(待 #24):shop 自助商店 URL、LINE handle(TG 群为私有,不放公开页)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 19:19:58 +08:00
wangjia c1c6b7ccf5 fix(website): Docs 卡片暂改不可点(去掉「阅读」死链)
4 张文档卡原为 <a class=doc> 无 href + 「阅读」死链;真实文档页未写。
改为信息卡(<div>),保留标题/描述与导航·页脚入口,不误导。
撰写真实文档(快速开始/FAQ/协议安全/隐私政策)并接回链接 → #31。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 18:09:07 +08:00
wangjia 75ed293151 fix(website): 移除无效的「登录」按钮(死 span,无链接)
Web 用户中心(web/usercenter)尚未部署、无 URL,登录入口暂无处可去;
产品主流程为 App 内登录。先删死按钮避免公开页点了没反应,待 #30 部署
usercenter 后接上登录地址恢复(已挂 #30 子任务)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 17:56:49 +08:00
wangjia 95abb74db2 fix(website): 价格按语言分币种 —— 英文版显示美元(P0)
PRICES 原为中英共用一份人民币值 → 英文页显示 ¥ 不专业。改为 Record<Lang,...>:
- zh: ¥0 / ¥25(年¥20) / ¥99(年¥79)
- en: $0 / $3.99(年$3.19) / $13.99(年$11.19)  年付沿用 8 折
Pricing.astro 接收 lang 按语言取价;Site.astro 传入 lang。
构建产物核实:EN 页 0 处 ¥、ZH 页 0 处 $;红线扫描 0 命中。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 17:43:44 +08:00
wangjia 1c8f6cb56b fix(website): 去除夸大宣传 + 隐藏占位 Blog 区(官宣页上线前 P0)
- 「80+ 全球加速线路」→ 中性「全球加速线路 / Global routes」(hero 徽章/功能卡/Pro 套餐/对比表 4 处);
  现实仅 1 个节点(pangolin1),80+ 属夸大宣传(国内 VPN 有法律风险)。
- 功能卡③描述去掉「港日新美欧韩」具体地区罗列 → 「覆盖多地优质线路」。
- 隐藏整个 Blog 区:3 篇均占位假帖,含虚构的「首尔/法兰克福节点」「v2.4 版本」。
  移除 <Blog/> + 顶栏导航 Blog 项 + 页脚 Blog 链接;有真实文章后恢复。

未动(待用户输入):英文版价格货币(等美元定价);下载链接归入 #30 CI/CD。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 17:35:49 +08:00
wangjia 08fb915b5e test(devices): #27 同机换账号登录注册 E2E(SQLite,服务层)
走 login 完全相同的路径 devices.Service.RegisterIfAbsent{MaxDevices:0}:
同一 device uuid 两账号各得一行、A 重登幂等刷新、共享 uuid 恰 2 行。
补 SQLite(生产同引擎)服务层覆盖,与既有 store 层 TestSQLite_DevicesUserScopedUUID
及 MySQL 集成 TestSameDeviceUUIDTwoAccounts 三层互证 F3 修复。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 13:11:25 +08:00
wangjia eb7c3c1062 feat(agent): WARP 域名分流 —— 命中域名走 Cloudflare 干净出口(#29)
节点 sing-box 渲染新增可选 WARP 分流:节点本地 warp.json(默认
<StateDir>/warp.json)配 WARP 凭证 + 域名清单 → 渲染时注入一个 userspace
WireGuard(WARP)endpoint + route(sniff 取 SNI/Host → domain_suffix 命中走
warp,其余 final=direct)。sing-box 1.11+ endpoints 语法,system=false 用户态
不依赖内核 wg 模块。

- 运营改域名只需编辑 warp.json + 重启 agent(sing-box 无热重载),即「配置的方式」。
- warp.json 不存在/enabled=false/域名空/凭证缺/坏 JSON → 一律按未启用,配置与旧
  节点逐字节一致,坏配置绝不产出无法启动的 sing-box config(渲染读失败仅记日志)。
- WARP 凭证节点私有(wgcf 注册免费匿名账号),不入 git、不经控制面。
- 测试:注入 endpoint+route/无配置无 route/禁用或残缺不注入/坏 JSON 优雅退化。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 13:40:00 +08:00
wangjia f035552ff5 fix: disconnect 吊销每设备凭证 + 客户端断开时真正调用(F4,#28)
审查 F4 两层问题:①DisconnectNode 只撤账户级 ent.DpUUID,而 connect 下发的是
每设备 devDp——吊销从未对准目标;②客户端从未调用过 disconnect 端点(只有
fetchConfig),端点是死代码,凭证一律活到 TTL(付费 24h)。

- server:disconnect 收 optional body {device_id},吊销该设备 dp_uuid(优先)+
  账户级遗留兜底;旧客户端无 body 走兜底,行为不回归。nil hub 守卫(测试友好,
  与 ListNodes 一致)。
- client:ConnectApi.disconnect(best-effort,5s 超时吞错);_disconnect 加
  revokeCredential 参数,仅在「不会紧接重连同节点」的路径置 true(用户主动断开/
  额度耗尽/登出)——看门狗断开→重连若也吊销,revoke 可能晚于新 connect 推送、
  误杀新会话。
- test:httpapi disconnect 三态(带 device_id 双吊销/无 body 仅兜底/设备已移除
  不炸);client 功能套件 182 过(golden 为已知 macOS 本地漂移,不相关)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 13:29:01 +08:00
wangjia 63d1baeb01 fix(server): devices 唯一键改 (user_id,uuid) —— 同机换账号不再 403 死结(F3,#27)
device_id 按安装持久、跨账号复用;旧全局 UNIQUE(uuid) 使同机第二账号注册永远
403 → ConnectNode 判 DEVICE_NOT_REGISTERED,提示的「重新登录」无法自救。

- migration 21(sqlite/mysql):UNIQUE(uuid)→UNIQUE(user_id,uuid);platform 放行
  linux(normalizePlatform 早已接受,旧 CHECK/ENUM 会拒)。SQLite 表重建用
  rename→重建→复制→drop 次序,单事务内不触发 sessions 的级联清空(FK ON)。
- 查找全部收口为按 (user,uuid) 作用域(重复 uuid 跨用户后全局查询歧义):
  findDeviceByUserUUIDTx / FindByUserUUID;Register 删跨用户 Forbidden 分支;
  Delete/ForceLogout/Rename 对他人设备返回 404(不可见);SessionActive 删
  「非本人 fail-safe」分支,dev==nil→false 语义不变。
- 测试:SQLite 真迁移库 F3 回归(两账号同 uuid 各自成行/同用户重复拒/linux 入库/
  sessions 重建后级联仍成立)+ MySQL 集成测试 schema 同步与双账号用例。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 13:16:40 +08:00