Commit Graph

8 Commits

Author SHA1 Message Date
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 aee9ba7b72 feat(client): 登录体验 — 记住邮箱 + 密码显隐 + 7天免登陆
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
1. 记住邮箱:登录页预填上次邮箱(TokenStore.saveLastEmail,登录/注册成功时存,
   退出登录不清除)。
2. 密码显隐:登录/注册密码框加眼睛按钮切换明文(PangolinIcons.eye/eyeOff)。
3. 7天免登陆:RootFlow 改为响应 authProvider——启动时有有效会话直接进主界面;
   AuthNotifier.refresh() 在服务端拒绝(refresh 过期/超 7 天)时 logout 回登录页,
   网络错误则保留会话。会话恢复期间显示极简启动屏。

flutter analyze 0 error;114 tests passed。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 06:28:49 +08:00
wangjia f20eddad55 feat(client): P4 节点实测延迟/设备/套餐价/兑换/统计接真(#6 6D)
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
- latency_probe.dart:并行 TCP 握手实测各节点 per-client RTT(服务端无法代知)。
- nodes_provider:解析 /v1/nodes 的 host/port,后台测延迟回填 Node.ping;智能选择
  按实测最小;去掉 8 个伪造演示节点(空列表用中性占位,不再伪造服务器)。
- Node:+host/port/copyWith/pingLabel(未测显示 —);各端 ping 显示改 pingLabel。
- account_screens:PlansScreen 接 /v1/plans(价格 priceLabel + me 标当前档);
  DevicesScreen 接 /v1/me/devices + 移除 DELETE + last_seen;RedeemScreen 接
  /v1/redeem(成功刷新 me,失败显示后端文案)。
- stats_page:周柱接 me.weekly_gb,本月流量/时长接 usage(30),延迟用生效节点实测。
- 顺带补登记早前 log_time.dart 的删除(log.dart 重构遗漏 stage)。

flutter analyze 0 error;115 tests passed;受影响 4 golden 重生成。
本机设备高亮 + 真实 device id 随 P6(device_info_plus)落地。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 00:04:25 +08:00
wangjia ba53a0d478 feat(client): P1 真实数据接入地基(#6 6A)
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
- api_config.dart:API 基址单源(消除各处重复 _kApiUrl)
- api_client.dart:统一鉴权 HTTP 客户端,401→AuthNotifier.refresh→重试一次,
  错误统一 AuthApiException;+ 单测覆盖 401 刷新重试
- 模型 me/device/plan/usage_point(对齐后端 snake_case 契约)
- account_api.dart:/v1/me、/v1/plans、/v1/me/devices(列表+删)、/v1/usage、
  /v1/redeem、/v1/ads/unlock 封装
- auth_provider:注入 AuthApi + refresh();account_providers 装配 ApiClient/AccountApi
- 顺带修复 onboarding 提交遗留的测试 stub(_NullTokenStore 缺 isOnboarded/markOnboarded)

flutter analyze 0 error;114 tests passed。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 23:32:57 +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 6979411c1d feat: 档2 接真后端 — mac app 真注册/登录/拉节点
dev 本机置备(新增 dev/):
- docker-compose.yml: MySQL :13306 + Redis :16379(避让本机已占的 3306/6379)
- run-local.sh 一键: 起容器→openssl 生成密钥(dev/.local gitignore)→migrate→
  seed→启动 server :18080(避让 8080);LogMailer 把验证码打日志
- seed.sql(1 provider + 1 HK 节点)、run-local.md 手册、.gitignore

后端两处修复(本地无 gRPC 场景,注释意图与代码不符的 bug):
- main.go: 无 gRPC 时真正构造 Hub-only nodeSvc(原只建 hub 没赋值),/v1/nodes 才能挂
- main.go: /me 改 Route 子路由根 Get,修 Get("/me")+Route("/me") 冲突致 404

客户端接真后端:
- token_store: MacOsOptions(useDataProtectionKeyChain:false) 修 keychain -34018
- auth_screen: 删 dev 旁路(test 账户内存登录),所有登录走真 API

验收: 后端 curl 端到端全通(验证码→注册→登录→/nodes→/me);
server go build/vet/test 通过, client analyze 0 + test 84

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 13:27:58 +08:00
wangjia 4dc4127252 feat(client): 三端布局架构 + macOS 桌面端 + app 图标
三端布局(mobile/tablet/desktop):
- core/responsive/form_factor.dart 形态判定 + shell/ 分发器(home_shell→desktop/mobile)
- desktop_shell 对照 ui_kits/desktop/dapp.jsx: 侧栏204·6项 + 套餐卡 + 顶栏(标题/状态/主题切换) + 连接页居中单列
- 新增组件 nav_sidebar / plan_badge_card / content_top_bar / bottom_tab_bar
- 新增一级页 contact_page / settings_page; navigation_provider(NavView)
- 删除旧 widgets/home_shell.dart(逻辑迁入 shell/)

macOS 桌面端:
- 窗口默认 920×600 + 最小 720×560(MainFlutterWindow.swift)
- app 图标替换为穿山甲(AppIcon.appiconset 全套, 由 app-icon.svg 渲染)

其余(本会话):
- Phase2 接线: auth_api/token_store/auth_provider/vpn_bridge_provider + 真实 connection/nodes
- lucide_icons 兼容补丁(packages/lucide_icons_patched) 修复 IconData final 报错
- 测试修复: connect_passthrough(UTF-8) / harness / golden @Skip
- l10n 新增 settingsTitle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 09:23:20 +08:00
wangjia bd8f974a25 feat(M6): 控制面联调 — mock connect server + 客户端 ConnectApi 透传
tsk_nuoKSM4Vt-zK

## 服务端(server/cmd/mockserver)
- `main.go`(79 行):独立 mock HTTP server,实现 POST /v1/nodes/:id/connect
  - 路径/请求体/响应体字段名与 §3.1 契约逐字一致
  - Bearer token 鉴权(-token 参数,空值跳过,不入库)
  - 缺少 device_id → 400;未授权 → 401;非 POST → 405
  - 返回完整 sing-box config JSON(tun + REALITY/Hy2 outbound + urltest + route/dns)
  - 注:mock 联调;待 #5/#6 真实 connect 接口就绪后替换
- `main_test.go`:6 项单测,覆盖 §3.1 结构校验、auth、method、path

## 客户端(client/)
- `lib/services/connect_api.dart`:ConnectApi 类
  - fetchConfig(nodeId, deviceId) → 原始响应体字符串(不做任何修改)
  - 错误路径:HTTP 非 200 / 超时 / 非法 JSON → ConnectApiException(含双语 message)
- `lib/services/vpn_bridge.dart`:VpnBridge stub(M6 联调,libbox 绑定待 11C)
  - start(configJson) 原样存储,不修改;stop() 清空
- `lib/widgets/home_shell.dart`:_toggle/_pick 替换为真实 API 流程
  - ConnectApi.fetchConfig → VpnBridge.start(透传,无中间变换)
  - 错误路径:ConnectApiException → SnackBar,status 回 off,无残留半开隧道
  - API URL/token/deviceId 由 --dart-define 注入(不入库)
- `lib/widgets/server_tile.dart`:ServerInfo 新增 nodeId 字段
- `pubspec.yaml`:新增 http: ^1.2.1 依赖
- `test/connect_passthrough_test.dart`:4 项透传断言单测
  - 核心:fetchConfig 返回字符串 === VpnBridge.start 接收字符串(逐字节相等)
  - 空格/格式原样保留(不经 jsonEncode 重序列化)
  - HTTP 错误、非法 JSON 错误路径覆盖

## 运行方式(M6 联调)
```
# 启动 mock server(无 auth)
go run ./server/cmd/mockserver -addr :8081

# 启动客户端(指向 mock server)
flutter run \
  --dart-define=PANGOLIN_API_URL=http://localhost:8081 \
  --dart-define=PANGOLIN_API_TOKEN=dev-mock-token \
  --dart-define=PANGOLIN_DEVICE_ID=demo-device-001
```

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:16:29 +08:00