feat(ds-flow): 前端设计系统治理重构(Flutter 五端 + Web 两端) #2
Reference in New Issue
Block a user
Delete Branch "worktree-macos-killswitch"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
ds-flow 前端设计系统治理重构(#19)
用 ds-flow 方法论把 Flutter 五端 + 官网 + 用户中心收口到「设计单源 · 代码镜像 · 静态闸拦漂移 · golden 双主题验收兜底」。摸底结论:pangolin 非从零 bootstrap(已约 65% 达标),本轮为补缺口 + Web 去重。主题保持 light/dark。
六阶段
design/prototype/(tokens 真源迁移 / atoms 45 原子 / icons 58 Lucide sprite / index.html 登记页 / serve)+ ui_kits 标 DEPRECATED + 真源指针收敛tools/check-l1-sync.mjs跨端同源闸(Web token 值 · 三端图标⊆原型 · Web 硬编码色)check_ds_code· 原型校验闸check-ds· CI 串联四闸 + golden 扩全量 · pre-commit 增挂条件闸 · 全景文档frontend-overview.html顺带修复的两个潜伏 bug
Future.delayed改为可取消 Timer,onDispose 一并取消(生产资源 + golden pending-timer 双修)。localeProvideroverride 类型错(该屏 golden 一直不可编译,因 CI 只跑 components+auth 潜伏)。验证
明确延后(非阻塞,前置=原型整屏 HTML,属 L3 新屏工作)
计划文档:
docs/frontend-ds-refactor-plan.html· 全景:docs/frontend-overview.html🤖 Generated with Claude Code
免费版此前形同虚设:ConnectNode 每次连接发固定 daily_minutes×1min TTL、 从不扣减已用,重连即崭新 10 分钟,日额度从未强制。改为账户级(全设备共享) 真卡控 + 累加式看广告加时: - migration 000020: usage_daily 加 ad_bonus_minutes(sqlite+mysql)。 当日额度 = plans.daily_minutes + ad_bonus_minutes;剩余 = 额度 − minutes_used。 - usage.store.AddAdBonusMinutes: 事务内累加封顶(替代布尔 MarkAdUnlocked), 返回新总额+本次实际加时;GetDay/GetUsageRange 补读 ad_bonus_minutes。 - usage.service.UnlockAd: verify+nonce 后 +adBonusPerAd(10) 封顶 adDailyBonusCeiling(120), 返回 granted+remaining;TodaySummary 加 MinutesCap/AdBonusMinutes。 - usage.ads DevVerifier: 放行式占位校验(nonce 防重放仍生效),接真 AdMob 前 让看广告加时流程可端到端跑通;main.go 按 ADS_DEV_MODE/默认装配。 - ads/unlock: 204 → 200 返回 {granted_minutes, minutes_remaining}。 - ConnectNode 免费门: 读 AccountDayMinutes(used,bonus) → remaining≤0 拒 QUOTA_EXHAUSTED, 否则 TTL=remaining(凭证到点硬切断兜底)。nodes.store 加 AccountDayMinutes。 - /me: 补 ad_bonus_minutes,quota_today_min 分母改 daily+bonus,加 quota_cap_min。 - quota.CheckFreeConnect 同步累加语义(免费基础 10 分钟不再需先看广告)。 - openapi + 契约/迁移版本/集成测试同步;新增 SQLite AddAdBonusMinutes 单测。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>审查 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>走 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>- 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- 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安全审查:run: 里内联 ${{ gitea.ref_name }} 有注入风险(恶意 tag 名);改经 env REF_NAME + 引用 "$REF_NAME"(同 deploy-server.yml 的 TAG 做法)。notify 的 job.status 同法。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u- AppLang enum 扩为 zh/en/ja/ko/ru/es;新增 strings_{ja,ko,ru,es}.dart (各 161 条,照 strings_en.dart 结构逐条翻译,无红线词,scan-redline 过) - app_providers: localeProvider 默认 AppLang.en(国际化默认);appTextProvider 三元改 switch 全覆盖 6 语 - AppLang.nativeLabel 扩展(各语言本地名);settings/account 两处 _LangSwitch 段控改下拉(6 语横排会挤) - flutter analyze lib 仅剩 2 个既有 withOpacity deprecation info 注:节点/套餐名、错误消息等 ~11 处数据驱动的 zh/en 三元暂回退 en(非崩溃, 后续可扩多语字段)。localeProvider 暂无持久化(沿用原设计,后续可加)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u- lib/i18n.ts: Lang/Entry 扩 6 语;STRINGS 130 条补 ja/ko/ru/es;makeT 回退英文 - lib/api/errors.ts: ERROR_TEXT 14 条补 4 语;bilingual 改为 code 本地字典优先 → 英文字典 → 后端 message → 通用兜底 - lib/theme.tsx: 默认语种 zh→en;localStorage 白名单扩 6 语;setAttribute('lang') 直用 lang - components/shared.tsx: LangSeg 段控 → 原生 select 6 语下拉 - app/layout.tsx: 默认壳 html lang zh→en;title/desc 改英文 - next build 通过(5 页静态);tsc 干净;无红线词 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u上一版判断错了:CF Pages _headers 不是'首个匹配生效',而是【叠加下发】——/* 和 /user/* 都匹配 /user/,两条 CSP 都发,浏览器对多条 CSP 取交集(最严)→ 官网严格 CSP 的 style-src(无 unsafe-inline)/connect-src('self')赢,用户中心内联样式被拦、 连 API 被拦 → 裸奔 + 登录失败。正解:/user/* 里 先删掉 /* 继承的官网 CSP,再设用户中心自己的(排 /* 之后,先加后删)。其余安全头沿用 /*。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u.githooks/pre-commit 在原有(红线/SQL/codegen-drift)基础上增挂三道 ds-flow 闸, 条件触发、秒级: - 原型校验(check-ds):动了 design/prototype/ 才跑 - 跨端同源(check-l1-sync):动了 prototype/ 或 web/{website,usercenter}/ 才跑 - Flutter 颜色单源(check_ds_code --changed):动了 client/lib/*.dart 才跑(只扫改动) install-hooks.sh 说明同步更新。启用仍是每台机一次性 `bash ci/install-hooks.sh` (不代跑,改本机 git config;CLAUDE.md 治理章节已注明)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>