wangjia
f97ae8186b
feat(client): Flutter 逐屏还原 + iPad ≥900 断点 (tsk_gpPXi-icyeOE)
...
对照 design/ui_kits mobile+tablet 原型逐屏补齐 Flutter 客户端:
- l10n 资源层(strings_zh/en,单显)+ Riverpod 状态层(连接状态机/免费额度/
节点选择/语言/主题),UI 与数据解耦,mock 数据接 API 不动 UI。
- 连接键严格三态(off 虚线轨道环 / connecting 旋转弧 / on 满环+计时+光晕),
状态来自 connectionProvider,点击只派发事件——禁止乐观显示。
- 节点页置顶「智能选择」推荐卡(clay 渐变 zap + 推荐胶囊,默认选中);
免费额度卡(剩余分钟+进度条 ≤3 分钟切 warning + 看广告解锁变绿)。
- Tab 左右滑动切换(手势竞技场仲裁,子页滚动不误触发,200ms 方向感知滑入)。
- iPad/宽屏 ≥900 LayoutBuilder 切侧栏分栏(导航行高 ≥48,连接页双栏/节点双列网格),
复用同一批原子组件,不 fork 页面。
- 语义 token 零硬编码;文案全部走 l10n;套餐数字对齐 §7;无支付表单/emoji 国旗。
- CI 红线词扫描扩展到 client/lib;新增 Flutter analyze+test CI 任务。
- 测试:连接状态机/额度/节点单测、连接键三态与卡片组件测试、
golden(连接键三态/推荐卡/额度卡 × 明暗)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-13 14:32:36 +08:00
wangjia
787151245e
merge: maestro/tsk_tFMU7-hKzfOf [tsk_tFMU7-hKzfOf] codes 激活码模块
...
解决与 1A 骨架的冲突:module 统一为 github.com/wangjia/pangolin/server
(codes 分支原用 pangolin/server,7 个源文件 import 已改写);
go.mod require 并集(redis 取 9.20.1);Makefile 以骨架为基底并入
build-codegen/test-unit/test-integration target。
go build/vet 通过,codes 单测通过。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-13 03:03:11 +08:00
wangjia
a139ef937a
merge: maestro/tsk_iRa5iBC4v7Tu [tsk_iRa5iBC4v7Tu] Flutter 客户端工程搭建(演示态跑通)
2026-06-13 03:01:31 +08:00
wangjia
8c516d4f2a
merge: maestro/tsk_FUQws_DMIcXa [tsk_FUQws_DMIcXa] mTLS/CA + 引导 token 框架
...
解决与 Go 骨架(1A)的 add/add 冲突:go.mod 统一 module 路径为
github.com/wangjia/pangolin/server,require 取并集;setup.sh 合并两边意图。
go build ./... 与 go test ./internal/mtls/... 通过。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-13 03:01:31 +08:00
wangjia
37b18d4293
merge: maestro/tsk_9uMrd9kUpmVA [tsk_9uMrd9kUpmVA] 数据面定稿 + 修订 ARCHITECTURE.md connect 契约
2026-06-13 02:57:25 +08:00
wangjia
2112230d57
merge: maestro/tsk_A1HkEdRbXLGY [tsk_A1HkEdRbXLGY] openapi.yaml 契约(13 操作 + 全量 schema)
2026-06-13 02:57:25 +08:00
wangjia
dc7a4f12d7
merge: maestro/tsk_P5b5nIrEsfrV [tsk_P5b5nIrEsfrV] CI 流水线
2026-06-13 02:57:25 +08:00
wangjia
3f0f82eca0
merge: maestro/tsk_L2k2VrEujof8 [tsk_L2k2VrEujof8] Go 模块骨架 + 工具链(go.mod/tools.go/Makefile)
2026-06-13 02:57:25 +08:00
wangjia
18869bc1fa
merge: maestro/tsk_paiqu21eZIuw [tsk_paiqu21eZIuw] MySQL migration 7 文件 + plans seed
2026-06-13 02:57:25 +08:00
wangjia
adfd8ce3f2
merge: maestro/tsk_bYv-CoKSwgAC [tsk_bYv-CoKSwgAC] 备份与灾备演练
2026-06-13 02:57:25 +08:00
wangjia
afcd7b325c
feat(codes): implement activation-code module (tsk_tFMU7-hKzfOf)
...
Implements server/internal/codes/ with all required functionality:
## Generator (generator.go)
- Crockford Base32 16-char codes (15 data + 1 Crockford mod-37 check char)
- crypto/rand for unbiased random generation with rejection sampling
- Canonicalize(): I/L→1, O→0 folding, hyphen/space stripping
- Hash(): SHA-256 of canonical plaintext (only value stored in DB)
- Algorithm hard-coded; check char detects all single-char substitution errors
## Store (store.go)
- MySQL-backed via database/sql
- CreateBatch / CreateCode with ErrDuplicate on UNIQUE conflict
- FindCodeByHashForUpdate: SELECT … FOR UPDATE for row-level concurrency control
- MarkRedeemed, ExtendSubscription, CreateSubscription, GetActiveSubscriptions
- WriteAuditLog, CodeExistsByHash
- BeginTx at READ COMMITTED (FOR UPDATE provides row exclusivity)
## Service (service.go)
- Redeem(): 9-step flow with full idempotency and concurrency safety
- isLocked / recordFail / clearFail via Redis key redeem:fail:{user_id}
- SELECT … FOR UPDATE → single winner under N-concurrent redemptions
- Same-plan: extends existing subscription (max(expires_at,now)+days)
- Cross-plan: creates new subscription (max(now,latest)+days)
- Idempotent: same user re-submits → 200 without re-applying
- 5 failures → ACCOUNT_LOCKED for 1 hour (sliding window via Redis)
- CreateBatch(): generates N codes, stores hashes, returns plaintext once
- Automatic retry on hash collision (birthday probability ≈10⁻⁸)
## Webhook (webhook.go)
- POST /webhook/store/codes — outside /v1, no JWT required
- HMAC-SHA256 with hmac.Equal constant-time comparison
- ±5 min timestamp window
- Redis SetNX nonce deduplication (15-min TTL)
- Idempotent: duplicate nonce → 200; duplicate code_hash → 200
## HTTP Handler (handler.go)
- POST /v1/redeem endpoint wired to Service.Redeem
- Reads userID from context key (set by JWT middleware from auth module)
- Bilingual error responses {code, message_zh, message_en}
## Export (export.go)
- ExportCSV(): streams plaintext codes to io.Writer as CSV
- Plaintext NEVER stored in DB; only SHA-256 hash persists
## CLI (cmd/codegen/main.go)
- codegen -plan -days -count -channel -note -dsn [-out]
- Transition tool until admin panel (#8 ) is ready
- Outputs CSV to stdout or file; warns operator about plaintext sensitivity
## Infrastructure (skeleton)
- internal/config/config.go: env-var configuration
- internal/db/db.go: MySQL connection pool helper
- internal/redisutil/redis.go: Redis client constructor
- internal/apierr/apierr.go: bilingual error types
- migrations/001_init.sql: DDL for codes module tables
- go.mod with all dependencies
## Tests
- generator_test.go (unit, no deps):
- Format, uniqueness (10k codes, zero collisions), normalization,
check-char detection of all single-char errors, hash consistency
- webhook_test.go (unit, no deps):
- Signature rejection, missing signature, stale/future timestamp,
missing nonce, constant-time HMAC comparison
- service_test.go (//go:build integration, testcontainers):
- N=20 concurrent redeemers → exactly 1 winner
- Idempotent redeem returns success for same user
- Other-user redemption → CODE_REDEEMED + failure counted
- 5 failures → ACCOUNT_LOCKED on 6th attempt
- Same-plan extension: expires_at precision assertion
- Cross-plan creation: new subscription row
- Audit log written on every successful redemption
- CSV export: no plaintext in DB, hash present
- Webhook nonce replay: idempotent 200
- Webhook same-hash: idempotent 200, single DB row
Run unit tests: make test
Run integration tests: make test-integration (requires Docker)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-13 02:16:16 +08:00
wangjia
526e7f2f33
feat(tsk_iRa5iBC4v7Tu): Flutter 客户端工程搭建(演示态)
...
基于 design/flutter/ 起步包在 client/ 下建立 Flutter 工程:
- pubspec.yaml:flutter_svg / lucide_icons / google_fonts 依赖;
SVG 资产注册;字体由 google_fonts 运行时加载,无需本地 ttf
- lib/pangolin_theme.dart:完整设计令牌(色阶 / 间距 / 圆角 /
动效 / 文字阶),PangolinText 改用 GoogleFonts getter,
PangolinTheme.light / .dark 开箱即用
- lib/main.dart:runApp + 明暗主题 + 登录 → 引导 → 主框架 RootFlow
- lib/widgets/:全套组件雏形
pangolin_icons Lucide 图标映射
pangolin_button 胶囊按钮四态
country_code 国家码块 + 信号条
status_pill 色点胶囊
connect_button 核心连接键三态(off/connecting/on + 动画)
server_tile 服务器列表行
plan_card 套餐卡(专业版渐变高亮)
auth_screen 登录 / 注册(邮箱验证码 + 设密码)
onboarding_screen 首次引导 PageView(3 屏可滑动 + 进度点)
home_shell 底部 4 Tab(连接/节点/统计/账户 + 状态机)
account_screens 套餐选择 / 设备管理 / 兑换 / 联系我们
pangolin_logo PangolinMark / BrandLockup / AppIcon(SVG)
- assets/:logo-mark / logo-mark-white / logo-wordmark / app-icon(SVG)
- android/:Kotlin 主 Activity + Manifest + Gradle 配置
- ios/:Runner.xcodeproj + xcscheme + Podfile + AppDelegate + storyboard
运行方式(cd client/):
flutter pub get && flutter run
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-13 02:15:42 +08:00
wangjia
73fd281bb5
feat(mtls): implement mTLS/CA + bootstrap token framework [tsk_FUQws_DMIcXa]
...
ECDSA P-256 self-signed CA with disk persistence (load-or-generate),
CSR signing (CN=nodeUUID, 90d validity, EKU=ClientAuth), one-time
bootstrap tokens via Redis GETDEL (15min TTL), CRL revocation with
Redis SET + DB interface, gRPC unary+stream interceptors that extract
CN from verified TLS chains (Enroll whitelisted, others require cert),
and NewServerTLSConfig (VerifyClientCertIfGiven + TLS 1.3 + CRL hook).
Frozen API: SignCSR / CAPEM / IssueToken / ConsumeToken / Revoke /
NewServerTLSConfig / UnaryServerInterceptor / NodeUUIDFromContext
Tests cover: CA sign+verify, token one-time guarantee, TTL expiry,
revocation rejection, interceptor whitelist (5 categories).
Redis layer backed by miniredis in tests.
Run setup.sh from server/ to fetch deps and verify tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-13 01:44:21 +08:00
wangjia
ab2bbaf683
tsk_9uMrd9kUpmVA: 数据面定稿 + 修订 ARCHITECTURE.md connect 契约
...
决策:libbox 统一承载 REALITY/Hy2 outbound(方案 C),弃用 WireGuard peer 注册。
现网 deploy/ 基础设施(singbox Hy2 + xray REALITY)已验证,零改动复用。
变更文件:
- design/server/ARCHITECTURE.md v0.2:
· §0 数据面描述改为 sing-box libbox (REALITY/Hy2)
· §1 拓扑图更新(wireguard+agent → singbox+agent,客户端标注 libbox 内嵌)
· §2 devices 表移除 pubkey;nodes 表改为 reality_public_key/short_id/uuid/hy2_password
· §3 connect 响应由 WireGuard 配置改为完整 sing-box config JSON
· §3.1(新增)connect 契约详细规范:四块 inbounds/outbounds/route/dns、
客户端透传原则、字段与 deploy/ 模板逐字段对照、占位符替换说明
· §4.3 连接流程更新(peer 注册 → config JSON 渲染下发)
· 附录 A(新增)v0.1→v0.2 变更汇总
- doc/plans/11-libbox-bridge.md(新建):
完整决策记录(背景/备选/结论/影响面),任务链 11A→11C→11D/E/F→11H
- doc/plans/client-connect-config.example.json(新建):
最小示例 config,字段与 deploy/ 模板对齐,可用 sing-box check -c 校验
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-13 01:43:17 +08:00
wangjia
58c6eb721e
feat: 新增 server/api/openapi.yaml — v1 API 完整契约(tsk_A1HkEdRbXLGY)
...
OpenAPI 3.0.3,覆盖 doc/02 §2 全部 15 个操作:
- 认证 4(sendVerificationCode/register/login/refreshToken,security:[])
- 账户 3(getMe/listDevices/deleteDevice)
- 商业 3(redeemCode/adsUnlock/listPlans)
- 节点 3(listNodes 含 304/connectNode/disconnectNode)
- 用量 1(getUsage)、公告 1(listNotices)
全量 schema:Error·TokenPair·Me·UserInfo·SubscriptionInfo·TodayUsageSummary·
Device·Plan·RedeemResult·NodeDirectory·Node·ConnectCredential·
RealityParams·Hysteria2Params·UsagePoint·Notice
安全约定:bearerAuth(JWT RS256) 全局;auth 四操作 security:[];
components/responses 统一 Error 引用;429 TooManyRequests 带 Retry-After;
节点目录不含密钥,密钥仅由 connect 端点下发;对外 ID 全部 uuid 格式。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-13 01:29:31 +08:00
wangjia
1629385c70
feat(server): Go 模块骨架 + 工具链 [tsk_L2k2VrEujof8]
...
新建 server/ Go 模块:
- go.mod: module github.com/wangjia/pangolin/server, Go 1.22
依赖: go-chi/chi/v5, google/uuid
- tools.go: //go:build tools 锁定 oapi-codegen / golang-migrate 版本
- cmd/server/main.go: chi router + GET /healthz → {"status":"ok"},监听 :8080
- cmd/migrate/main.go: 占位入口(实逻辑归 1e)
- internal/{config,store,apierr,idgen,auth,codes,devices,nodes,usage,admin}/doc.go
各包职责说明
- Makefile: build/test/vet/lint/generate/migrate-up/migrate-down
- .golangci.yml: govet/errcheck/staticcheck/revive/gofmt 基线
- README.md: 定位 + 目录树 + make 入口说明
- setup.sh: 首次 go mod tidy 引导脚本(go.sum 需运行后生成)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-13 01:28:37 +08:00
wangjia
b4114c93ee
feat(db): add MySQL migrations 000001-000007 covering all 12 tables + seed [tsk_paiqu21eZIuw]
...
- 000001: users + devices(账户 / 设备,含 argon2id pw_hash、dp_uuid)
- 000002: plans + subscriptions(套餐口径来自 design/CLAUDE.md §7)
- 000003: code_batches + codes(SHA-256 code_hash,明文不入库)
- 000004: usage_daily + audit_log(最小数据原则 + JSON meta)
- 000005: providers + nodes(providers 先建,nodes FK 引用)
- 000006: node_events(9 值 ENUM)+ directory_version(CHECK id=1,MySQL ≥ 8.0.16)
- 000007: plans 三行 seed + directory_version(1,1),ON DUPLICATE KEY UPDATE 保可重入
所有表 ENGINE=InnoDB DEFAULT CHARSET=utf8mb4,时间列 DATETIME(6),
主键 BIGINT UNSIGNED AUTO_INCREMENT + 对外 uuid CHAR(36) UNIQUE。
每对 down 文件逆序 DROP(子表先于父表)。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-13 01:28:01 +08:00
wangjia
c92cd11cc5
feat: CI 流水线 — lint + 单测 + OpenAPI 校验 + 脱敏扫描 + 镜像构建 [tsk_P5b5nIrEsfrV]
...
新增 .gitea/workflows/ci.yml 五个 Job:
1. lint — shellcheck -S warning 扫描全部 deploy/ shell 脚本
2. unit-test — docker-compose config 语法校验 + nginx -t(桩证书)
3. openapi-check — openapi-spec-validator 验证 design/server/openapi.yaml
4. redline-scan — ci/scan-redline.sh 扫描 UI 文案红线词(design/ jsx/dart/html)
5. image-build — docker build pangolin-edge:ci
附带:
- ci/scan-redline.sh:脱敏扫描脚本,过滤注释行与外部渠道 handle
- ci/nginx-test.sh:自签桩证书 + nginx -t,CI 免依赖真实 Let's Encrypt
- design/server/openapi.yaml:依据 ARCHITECTURE.md §3 展开的 OAS 3.0 完整契约
- dparts.jsx / parts.jsx:修复 killSwitchSub EN 文案「the VPN drops」红线词
→ 改为「connection drops」(行为描述,不提产品类别)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-13 01:19:28 +08:00
wangjia
20dbb5f363
feat(backup): 备份与灾备演练系统 [tsk_bYv-CoKSwgAC]
...
实现 xtrabackup + binlog 增量备份、age 加密异地存储与月度 DR 演练。
## 交付内容
### deploy/backup/ —— 备份容器
- Dockerfile:Ubuntu 22.04 + xtrabackup 8.0 + age v1.2.0 + AWS CLI v2 + pg_client
- entrypoint.sh:初始化 AWS 凭据 / age 公钥,启动 crond
- crontab.txt:全量 02:00 / 增量每 15min / DR 演练每月 1 日
- scripts/lib.sh:日志 / age 加密 / S3 上传 / 连接测试等共用函数
- scripts/backup-full.sh:MySQL xtrabackup 全量 + pg_dump 全量 + SQLite 备份
- scripts/backup-inc.sh:MySQL xtrabackup 增量 + binlog flush & 归档 + pg_dump 快照
- scripts/restore.sh:从 S3 下载、解密、prepare / pg_restore 完整恢复
- scripts/dr-drill.sh:月度演练 —— RPO 验证 / 备份完整性 / MySQL 临时容器恢复 / RTO 度量
- scripts/verify-rpo-rto.sh:实时 RPO/RTO 状态检查(S3 最新备份时间戳)
- backup.env.example:配置模板(MySQL / PG / S3 / age 公钥)
### 修改已有文件
- deploy/docker-compose.yml:新增 pangolin-backup 服务
- deploy/scripts/gen-secrets.sh:幂等生成 age 密钥对 + backup.env 模板
- .gitignore:排除 age 私钥 / backup.env
- deploy/README.md:容器一览 + 备份快速命令
### docs/备份与灾备.md
S3 初始化、IAM 权限设计、age 密钥管理、MySQL 权限、DR 恢复步骤、RTO 测量标准
## 设计要点
- RPO ≤ 15min:每 15min xtrabackup 增量 + binlog flush + pg_dump 快照
- RTO ≤ 4h:月度演练度量,估算恢复时长约 2h
- 独立身份账号:备份 IAM 仅有 s3:PutObject,与生产账号完全隔离
- age 加密:公钥存服务器,私钥离线保存,S3 中无明文
- 无 crontab:cron 在容器内运行,绕过 ec2-user crontab 限制
- 幂等部署:backup.env 存在则跳过,age 密钥对存在则跳过
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-13 01:09:18 +08:00
wangjia
a642bf16a2
feat: 同步 design/ 设计系统(含 iPad tablet kit) + 架构任务拆分(todo/)
...
design/ 同步自最新设计导出,新增 ui_kits/tablet/ 平板分栏布局;todo/ 录入 18 个并行实施任务。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 23:57:57 +08:00
wangjia
3ae96ef229
docs: 新增整体架构设计文档(doc/,HTML 七章)
...
覆盖前端五端/后端/MySQL/弹性节点拓扑/Web安全/安全总纲,遵循 design/ 设计系统视觉呈现。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 23:57:49 +08:00
wangjia
4bff8593e7
docs: 新增 CLAUDE.md(部署架构与主机约束指引)
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-05-30 23:30:08 +08:00
wangjia
73e026a4df
edge 容器自带证书续期(certbot in nginx 镜像)
...
deploy-pangolin / deploy (push) Has been cancelled
- edge 改为自定义镜像:官方 nginx + certbot + dns-cloudflare 插件
- pangolin-entrypoint.sh:后台每12h certbot renew,续期后本容器内 nginx -s reload
- 不再需要宿主 cron / docker socket / 独立 certbot 服务做日常续期
- /etc/letsencrypt 改 rw 挂载,挂入 cloudflare.ini
- compose 去掉 build:(宿主 docker-compose 不支持 compose build),改脚本 docker build
- deploy.sh/cutover.sh: docker build -t pangolin-edge:local 后再 up
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-05-30 23:10:17 +08:00
wangjia
39e7f2fc11
回滚:rollback.sh + docs/回滚手册.md
...
deploy-pangolin / deploy (push) Has been cancelled
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-05-30 17:09:00 +08:00
wangjia
1bb3a26f0c
cutover: 失败自动回滚到宿主 nginx
...
deploy-pangolin / deploy (push) Has been cancelled
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-05-30 16:52:05 +08:00
wangjia
fa3954724c
print-clients: 修正提示路径为 ~/pangolin
...
deploy-pangolin / deploy (push) Has been cancelled
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-05-30 16:45:15 +08:00
wangjia
1a346c32c1
方案C:分发层容器化(edge nginx)+ Hy2 + DNS-01 续期
...
deploy-pangolin / deploy (push) Has been cancelled
- edge: 容器化 nginx 接管 80/443,逐字移植现网 vhost(blog/jiu/marzban/pay)+ stream SNI 分流
- singbox: Hysteria2(host 网络,UDP 443)
- certbot: DNS-01(Cloudflare)续期,容器化
- xray: REALITY 仅 profile newnode(本台沿用 Marzban)
- deploy.sh 幂等且切换感知;cutover.sh 一次性停宿主 nginx 切容器(可秒级回滚)
- 弃用方案A 的 host nginx-apply/sudoers/root-setup
- 部署目录改 ~/pangolin(免 root);CI 注入 CF_API_TOKEN
EC2 实测:edge nginx -t 通过(真实证书)、compose 校验通过
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-05-30 16:42:30 +08:00
wangjia
bc77e85b7d
root-setup: 同时创建并授权 /opt/pangolin 部署目录
...
deploy-pangolin / deploy (push) Has been cancelled
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-05-30 11:52:32 +08:00
wangjia
cf96989e01
Phase 0: 容器化数据面 + CI/CD 自动部署
...
deploy-pangolin / deploy (push) Has been cancelled
- deploy/: Xray VLESS+REALITY (11443, 经宿主 nginx SNI 分流) + sing-box Hysteria2 (UDP 443) docker-compose
- 幂等 deploy.sh / gen-secrets.sh / print-clients.sh;受限 nginx-apply 特权脚本 + 一次性 root-setup
- .gitea/workflows/deploy.yml: NAS runner 经 SSH 远程部署到 EC2
- docs/ plan/: 设计方案与实施计划
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-05-30 11:48:47 +08:00
wangjia
833b8365da
Initial commit
2026-05-29 23:53:38 +00:00