Commit Graph

443 Commits

Author SHA1 Message Date
Abner
07e048679f 🐛 fix: 修复 Google Tag Manager 脚本标签重复闭合导致的加载错误
- 移除 index.html 中 Google gtag.js script 标签末尾多余的 `</script>`,避免 DOM 结构异常
- 保留原有 `defer`、`src`、`crossorigin` 配置,确保延迟加载逻辑不被破坏
- 删除冲突的属性 `integrity` 相关内容,配合当前实际加载方式恢复正常请求行为
2026-06-13 22:14:21 +08:00
Abner
0b6103a96a ♻️ refactor: 提取通知系统初始化代码为独立模块
- 新增 `src/js/init-notification.js` 模块,将原本内联在 HTML 中的通知系统初始化逻辑独立封装,便于维护和测试
- 在 `index.html` 中移除内联的 `<script type="module">` 代码块,改用 `<script>` 标签引用新模块,保持 DOM 加载后自动初始化行为不变
- 调整内容安全策略(CSP),移除无用的 `sha256-*` 哈希值,同时添加 `fonts.googleapis.com` 和 `fonts.gstatic.com` 以支持网络字体加载,提升资源兼容性
- 保持功能完全一致,仅进行代码结构重组和 CSP 规则细化,不引入任何新功能或逻辑变更
2026-06-13 22:03:06 +08:00
Abner
8bd2bdb8a4 ♻️ refactor: 提取初始化脚本为独立模块并清理过时架构文档
- 将 giffgaff 和 Simyo 页面的国际化初始化内联脚本提取为独立的 `init-i18n.js` 模块
- 将通知系统初始化内联脚本提取为独立的 `init-notification.js` 模块
- 移除 docs 目录下的 ARCHITECTURE.md 和 FUTURE_ARCHITECTURE.md 过期架构文档
- 更新 giffgaff_modular.html 和 simyo_modular.html 以引用新的独立脚本模块
2026-06-13 21:58:09 +08:00
Abner
3eb8550631 📝 docs: 精简用户文档并移除冗余技术参考
- 精简 README.md/README_EN.md 技术架构部分(-74%)
  - 移除详细技术栈列表(Webpack、Babel、Jest 等)
  - 改为 6 个用户友好的特性卖点
  - 保留开发者文档引导链接
- 修复中英文切换链接(添加 ./ 前缀)
- 更新 docs/CLAUDE.md 文档索引(根据实际存在文件)
- 移除冗余技术参考文档
  - 删除 docs/ARCHITECTURE.md
  - 删除 docs/FUTURE_ARCHITECTURE.md
  - 删除 docs/reference/ 目录(4 个文件)
  - 删除 docs/guides/CORS_SOLUTION.md
  - 删除 docs/fixes/ 目录(3 个文件)

变更影响:
- 用户 README 更简洁,聚焦功能价值
- docs/ 目录仅保留必要的用户教程和开发者指南
- 文档定位更清晰(用户文档 vs 开发者文档)
2026-06-13 21:57:13 +08:00
Abner
83bfe204f6 🐛 fix: 修复对 process.env.NODE_ENV 的访问以避免在浏览器环境报错
- 在 `performance-monitor.js` 中增加 `typeof process !== 'undefined'` 前置检查,防止浏览器中 `process` 未定义导致引用错误
- 在 `logger.js` 中移除 `process.env?.NODE_ENV` 的可选链,改为直接访问 `process.env.NODE_ENV`,与已有的 `typeof process` 检查保持一致
- 统一两模块中对 `process` 的判断逻辑,提高代码在非 Node.js 环境下的稳健性
2026-06-13 21:25:25 +08:00
Abner
44b05221f7 ♻️ refactor: 提取公共脚本为独立文件并移除内联 JS
- 将 Giffgaff 和 Simyo 页面中的内联 Bootstrap 错误守卫脚本提取为独立的 `bootstrap-guard.js` 文件,减少 HTML 体积并避免重复代码
- 将内联的入场动画(IntersectionObserver 滚动触发 `.reveal` 效果)提取为独立的 `reveal-animation.js` 文件,两个站点共用同一模块
- Giffgaff 页面中复制激活码和 SSN 按钮从 `onclick` 内联事件改为通过 `data-target` 属性与 `bindCopyCodeButtons` 方法绑定,支持安全上下文 Clipboard API 及降级方案
- 移除 `design-system.css` 和 `animations.css` 的 preload 预加载逻辑,改为直接引用静态样式表,简化 HTML 头部复杂度
- CSP 策略中将 `'unsafe-inline'` 从 Simyo 的 `script-src` 中移除,改用 hash 白名单替代,提升安全性
2026-06-13 21:23:03 +08:00
Abner
4286069759 feat: 调整 Sentry 与 CSP 以满足更严格的安全策略
- 将内联 Sentry DSN 配置改为外链脚本(/src/js/sentry-config.js),避免 CSP 限制导致初始化失败
- 更新 index.html 的 Content-Security-Policy,新增脚本 hash 以允许必要的内嵌代码,同时保留对 Sentry/Google 等域的连接权限
- 移除 CSP 触发风险较高的 preload+onload 写法,改为直接加载 Bootstrap 与本地样式文件,确保样式在受限环境下可用
- README 与 README_EN 的语言切换入口顺序调整,统一中英文版本跳转位置以提升可读性
2026-06-13 21:13:54 +08:00
Abner
5779d53995 🐛 fix: 修复异步生成二维码前未清空旧容器导致的显示不一致问题
- 在调用 generateQRCode 前先清空旧二维码容器
- 防止异步生成期间旧二维码和新 LPA 字符串不一致
- 修复 Session 恢复或重试时可能扫描到错误二维码的问题

影响模块:
- src/giffgaff/js/modules/ui-controller.js (showESimResult)
- src/simyo/js/modules/ui-controller.js (showQRResult)

问题来源:Bot 代码审查 (coderabbitai + cubic-dev-ai) P2 级别问题
2026-06-13 20:36:32 +08:00
Abner
7651972cab Merge pull request #76 from Silentely/fix/qrcode-local-generation
fix: 修复 Session 恢复后二维码和 LPA 不显示的问题
2026-06-13 20:34:31 +08:00
Abner
51e50b62ff 🐛 fix: 修复 QR 码生成中的标签解构与错误处理问题,增强安全性
- 修复 `createQRCodeContainer` 函数中 `labels` 参数为 `undefined` 时导致解构崩溃的问题,添加默认空对象
- 简化 QR 码库加载逻辑,移除脚本已存在且加载完成时直接返回的冗余检查
- 移除 `showQRResult` 方法中冗余的 `.catch()` 处理,错误已由 `generateQRCode` 内部统一处理
- 在 Netlify 函数中创建 sanitizedError 对象,避免包含 LPA 字符串的错误信息泄露到 Sentry 日志
- 更新测试用例以匹配重构后的函数名和错误消息,并移除无效的 jest mock 配置
2026-06-13 20:15:23 +08:00
Abner
5450a0ddb5 test: 增强二维码生成参数校验与错误处理测试,优化UI错误提示
- 为客户端 `generateQRCodeLocal` 增加非整数/越界size、非字符串data的输入校验测试用例
- 为后端 `qrcode-generate` 函数增加size整性、越界及非字符串data的测试用例
- 为后端 `qrcode-generate` 增加QRCode生成失败返回500的测试用例,并重构测试中Mock方式为全局Mock
- 在两个UI控制器移除废弃的 `qrTimeoutId` 字段,避免未捕获Promise拒绝并改用DOM API安全创建错误提示
- 前端 `generateQRCodeWithFallback` 增加后端超时(AbortError)和无效响应JSON的降级错误测试
2026-06-13 19:57:03 +08:00
Abner
0152277b4d 🐛 fix: 修复二维码生成 Promise 挂起、XSS 风险和 i18n 硬编码问题
- 修复 loadQRCodeLibrary Promise 永久挂起(Issue #75 根因)
  - 检测已存在脚本标签,移除失效脚本并重新加载
  - 添加 5 秒超时保护,防止 Session 恢复后二维码冻结
  - 统一 cleanup 函数清理监听器和定时器

- 修复 XSS 风险
  - 使用 DOM API 创建错误提示,避免 innerHTML 注入
  - 移除模板字符串 ${t(...)} 直接注入

- 修复 i18n 硬编码
  - createQRCodeContainer 接受 labels 参数支持多语言
  - generateQRCode* 函数支持 labels 透传
  - UIController 传入 tl() 翻译后的可访问性标签

- Schema 验证优化
  - qrcode-generate 保留手动验证,确保 405 优先级
  - 添加注释说明设计意图

- 代码规范
  - tests/security/qrcode-generate.test.js 添加 'use strict'

 测试: 8 passed, 0 failed
2026-06-13 19:43:50 +08:00
Abner
d694944e22 feat(qrcode): 添加二维码生成监控指标
- 新增 trackQRCodeEvent() 函数统一上报事件
- 上报到 Sentry(失败事件)
- 上报到 window.__esimAnalytics(成功和失败)
- 追踪指标:
  * 二维码生成成功率
  * 本地生成 vs 后端降级比例
  * 错误率和耗时
2026-06-13 19:31:35 +08:00
dosubot[bot]
0a6473c2a2 docs: Dosu updates for PR #76 2026-06-13 11:27:07 +00:00
Abner
f5c5dee5ab 🐛 fix: 修复 Session 恢复后二维码和 LPA 不显示的问题
问题:
- 用户完成 eSIM 激活后,因外部二维码服务不可用导致页面空白
- Session 恢复时未调用 showESimResult() 显示二维码和 LPA 信息

解决方案:
1. 新增通用二维码生成模块 (src/js/modules/qrcode-generator.js)
   - 实现三层降级策略:本地 CDN → 后端 Function → 文本提示
   - 消除对外部服务的依赖,提升隐私保护

2. 新增后端 Function (netlify/functions/qrcode-generate.js)
   - POST /bff/qrcode-generate 接口
   - 返回 base64 编码的 PNG 二维码
   - withAuth 中间件保护 + 输入验证

3. 重构前端二维码生成逻辑
   - Giffgaff/Simyo 统一使用 generateQRCodeWithFallback()
   - 保留并发调用防护和 tooltip 交互
   - 使用 i18n 翻译替代硬编码错误提示

4. 修复 Session 恢复逻辑
   - 在 handleSessionRestore() 中调用 showESimResult()
   - 确保刷新页面后二维码和 LPA 正常显示

技术改进:
- 懒加载 qrcode.js(~13KB gzip),仅在首次调用时加载
- 完整的测试覆盖(前端单元测试 + 后端安全测试)
- 更新 BFF 路由配置(Edge Function + 本地开发服务器)

Closes #75
2026-06-13 19:20:22 +08:00
Abner
4a7e842d40 🐛 fix: 修复 giffgaff session 恢复后 LPA 和二维码不显示的问题(Issue #75)
- `showESimResult` 中移除 `!state.lpaString` 的提前返回,即使 lpaString 为空也显示结果容器,确保用户看到明确的错误提示和可能的排查信息
- 在 session 恢复流程(`giffgaff-app.js`)中显式调用 `uiController.showESimResult()`,使刷新页面后能正确显示二维码和 LPA 字符串,而不再跳过关键渲染步骤
- 为 `showESimResult` 添加完整的 try-catch 包裹及防御性 DOM 元素校验,在二维码生成失败时降级显示错误提示,避免页面静默失败
- 新增 i18n 键 `giffgaff.app.error.lpaStringMissing`(中英文),用于替代硬编码的错误文案,保持国际化一致性
- 新增 `tests/giffgaff/session-restore-lpa.test.js`,覆盖正常 LPA 展示、空 lpaString 错误降级、二维码生成、状态不一致降级以及延迟显示等场景
2026-06-12 23:54:05 +08:00
Abner
2ff08dafae 🐛 fix: 扩展注入 require 到纯 ES Module 环境的异常过滤
- 在 sentry-loader 的噪音过滤规则中新增 `require is not defined` 的正则匹配
- 专门针对浏览器扩展将 CommonJS `require` 注入纯 ES Module 环境导致的报错提示进行降噪
- 保持原有已知浏览器噪音与第三方噪音规则不变,避免影响其他错误捕获
- 仅增加过滤项,降低对现有 Sentry 上报链路的改动范围
2026-06-11 09:22:40 +08:00
Abner
af2b024d99 📝 docs: 补充表单占位符及卡片的 i18n 翻译条目
- 为 DOM_TRANSLATIONS 新增多个表单输入框的 `placeholder` 属性翻译选择器,包括邮箱验证码、短信验证码、回调 URL、Cookie、手动激活码和 SSN 输入框
- 为 DOM_TRANSLATIONS 新增 `.current-time-card`、`.step-indicator` 及多个步骤卡片的 `aria-label` 翻译选择器,提升无障碍访问支持
- 在中文和英文 TRANSLATIONS 对象中分别新增对应的 12 条翻译键值,涵盖表单占位符文案和卡片无障碍标签
- 新增日语区段的留空占位结构,保持与中英文结构一致性(已通过 MASKED_FALLBACK 处理)
2026-06-08 18:39:23 +08:00
Abner
37923b523f 🎨 style: 更新 eSIM 激活文案以更准确描述流程
- 调整“短信验证码校验成功”的提示语,将“开始自动预订与激活”改为“正在自动更换 SIM 卡”,提升流程描述一致性
- 更新英文文案中“预订空 eSIM”部分的表述结构为“Reserves an empty eSIM”,使含义更明确
- 保持中文“实体卡转 eSIM 或设备更换…手动前往官网确认激活”的原文不变,仅对英文翻译与相关状态文案进行精修
- 影响范围限定在 i18n 数据文件内的文本替换,不涉及业务逻辑改动
2026-06-08 18:19:27 +08:00
Abner
e669b96f28 📝 docs: 完善 CLAUDE.md 检索指南并更新激活方式描述文案
- 在 CLAUDE.md 中新增基础检索章节,明确交叉验证策略、工具调用顺序、使用场景优先级及参数调优指南
- 新增网络检索章节,定义 smart-search-cli 工具路由矩阵、执行策略、错误恢复机制和核心约束
- 更新手动激活和短信验证码激活的中文及英文描述文案,明确说明适用场景为实体卡转 eSIM 或设备更换
- 同步更新 i18n-data.js 和 giffgaff_modular.html 中的对应文案,保持中英文翻译一致
2026-06-08 18:11:16 +08:00
Abner
5d77272f92 🐛 fix: 过滤 Sentry 中的钱包扩展序列化噪音
- 在 `beforeSend` 中新增对 `event.extra.__serialized__` 的检查,补充拦截非 `Error` 类型 rejection 被 SDK 序列化后的原始内容
- 通过统一的扩展关键词匹配逻辑识别钱包扩展相关噪音,避免仅依赖 `exception.value` 导致漏过滤
- 分别在 `sentry-init` 和 `sentry-loader` 两条初始化链路中同步新增处理,保证不同加载方式下行为一致
- 记录被过滤事件的来源、截断后的消息内容和机制类型,便于后续排查与统计
2026-06-07 17:49:14 +08:00
Abner
274d825ae8 🔧 chore: 更新 AI 使用指引的上下文检索要求
- 将任务开始前的检索要求调整为必须同时调用 `mcp__ace-tool__search_context` 和 `mcp__fast-context__fast_context_search`,提升上下文确认的可靠性
- 明确两个检索工具需要交叉对比结果并取交集作为有效上下文,减少单一检索带来的偏差
- 补充当两边结果不一致时的处理方式,要求进一步加深检索或调整查询词重新搜索
- 保留并顺延原有的 Giffgaff OAuth 说明,避免前端跳转地址与服务端环境变量的职责混淆
2026-06-05 17:08:51 +08:00
Abner
de0ca5b0df feat: 扩展关键词检测增强,兼容嵌套 reason 对象
- 在 sentry 事件处理流程中新增 `allReasonText` 收集逻辑,通过 `JSON.stringify(reason)` 获取嵌套错误对象的完整内容用于关键词检测
- 为 `JSON.stringify` 失败场景(如循环引用)增加降级方案:遍历 reason 的值并拼接字符串,尽量保留关键词覆盖范围
- 在 `message/stack` 检测失败后,增加对 `reason` 全量属性值的二次 `includesExtensionKeyword` 判断,提升钱包扩展错误识别准确性
- 分别在 `src/js/sentry-loader.js` 与 `src/js/modules/sentry-init.js` 中同步实现,保证不同初始化/加载路径的行为一致
2026-06-02 23:22:59 +08:00
Abner
af98d0339a Merge pull request #71 from Silentely/cv-main-gv6ncr
🐛 fix: 增强钱包扩展冲突检测 [skip ci]
2026-06-02 23:12:00 +08:00
Abner
8e6227e746 📝 docs: 为 giffgaff 和 simyo 模块新增 CLAUDE.md 文档并更新根目录索引
- 新增 `src/giffgaff/CLAUDE.md`(151 行),完整记录 Giffgaff 模块职责、模块结构、对外接口、OAuth PKCE 和 eSIM 激活数据模型、配置与状态结构
- 新增 `src/simyo/CLAUDE.md`(154 行),完整记录 Simyo 模块职责、模块结构、登录与设备更换流程、与 Giffgaff 模块的差异对比、配置与状态结构
- 更新根目录 `CLAUDE.md` 变更记录,新增 2026-06-02 增量扫描条目,修正构建脚本数量从 23 更新为 22
- 修复模块结构图中 giffgaff 和 simyo 节点的 click 链接,从 `MODULE_ARCHITECTURE.md` 重定向到新建的 `CLAUDE.md`
2026-06-02 23:06:48 +08:00
Abner
00a0e58d81 📝 docs: 更新架构与安全文档并扩展测试模块清单
- 更新 CLAUDE.md、docs/ARCHITECTURE.md 中的模块与阶段状态,补充 verify-cookie/health/public-config/notifications 等新节点,并将基础设施阶段由规划中标注为进行中
- 完善 docs/SECURITY.md 的 BFF 代理层与 withAuth 中间件说明,明确 x-esim-key 注入、函数白名单校验以及 CORS/体验验证的统一错误格式化策略
- 更新 src/giffgaff/MODULE_ARCHITECTURE.md 的安全考虑条目,补充 cookie 有效性检查间隔、CSP/Helmet 策略、HTML sanitization(escapeHtml/escapeAttr)以及通过 BFF 代理转发的传输层防护细则
- 扩展 tests/CLAUDE.md 测试范围与表格结构,新增 modules、giffgaff、security 三类的独立测试文件清单,并补充 tests/setup.js 的全局初始化说明
- 调整脚本与模块文档:scripts/CLAUDE.md 新增部署与构建相关脚本清单;src/js/modules/CLAUDE.md 更新 CaptchaManager 集成说明与模块覆盖状态,补充 notification-service/browser-utils 等相关条目
2026-06-02 22:50:26 +08:00
Abner
95650fa02a 🐛 fix: 增强 unhandledrejection 中钱包扩展冲突检测的 reason 对象属性值扫描
- 在 `sentry-loader.js` 和 `sentry-init.js` 中新增 `allReasonText` 变量,收集 reason 对象的所有属性值拼接为字符串,用于更全面的扩展关键词检测
- 钱包扩展可能 reject 如 `{code: 4001, message: "wallet must has at least one account"}` 这样的对象,仅检查 `message` 和 `stack` 可能遗漏关键词,新增遍历 reason 对象所有属性值的逻辑
- 在 `isExtensionProviderConflict` 检查之后,额外对 `allReasonText` 执行 `includesExtensionKeyword` 检测,确保即使关键词出现在非 message/stack 属性中也能被拦截
- 使用 try-catch 包裹属性遍历逻辑,防止不可遍历的对象导致二次异常上报
2026-06-02 22:42:06 +08:00
Abner
49dac32a9e Merge pull request #65 from Silentely/fix/bff-security-hardening
fix(auth): harden BFF routing and preserve internal calls
2026-06-02 22:22:48 +08:00
Abner
75cbed8ca8 📝 docs: 更新 OAuth 回调URL获取与回切页面说明
- 将 OAuth 登录后切回的目标页面从“eSIM 更换页面”调整为“eSIM-Tools 工具页面(本页面)”
- 同步更新中英文用户指南中开发者工具打开与控制台查找回调URL的步骤描述
- 修改页面内中英文文案:说明新窗口打开后应切回本页面,并在对应输入框处理回调
2026-06-02 00:33:32 +08:00
Abner
be5cd3940b feat: 支持 eSIM 激活流程中 ref 过期自动重发验证码
- 在 `giffgaff-mfa-validation.js` 中将 400 错误细分为 ref 过期(返回 410)和验证码错误(返回 400),通过正则匹配 `error`/`message`/`error_description` 字段识别 ref 过期场景
- 在 `esim-service.js` 的 `smsActivateFlow` 方法中捕获 410 错误后自动调用 `mfaHandler.sendSimSwapMFAChallenge()` 重新发送验证码,并抛出 `REF_REFRESHED_NEEDS_NEW_CODE` 错误码供 UI 层识别
- 在 `giffgaff-app.js` 的错误处理分支中新增对 `REF_REFRESHED_NEEDS_NEW_CODE` 的判断,清空验证码输入框并自动聚焦,引导用户输入新收到的验证码
- 在 `i18n-data.js` 的中英文翻译文件中分别添加 `giffgaff.esim.log.refExpiredRetrying` 和 `giffgaff.esim.errors.refExpired` 日志与错误提示文案
- `validateMFACodeForSwap` 方法在错误中附加 `statusCode` 字段,使上层能够根据状态码区分不同错误类型并执行对应恢复策略
2026-06-02 00:26:46 +08:00
Abner
d9dfc73986 📝 docs: 全面更新和完善项目文档内容
- 简化 README.md 的标题和章节样式,移除冗余 emoji 图标,添加原生 ES6 模块架构说明,调整描述文字使其更简洁清晰
- 优化了四个语言版本的 QR Code API 文档(中英文 Giffgaff 和 Simyo),将原来的简单描述更新为详细的三级回退链说明,包含每个服务商的 5 超时机制和 LPA 字符串降级方案
- 在 docs/ARCHITECTURE.md 中新增模块依赖关系图和关键设计决策说明,补充 BFF 模式、无框架设计、中间件统一等架构要点
- 将英文用户指南中的视频和提示信息移至附录章节,改进文档阅读体验;删除中英文用户指南顶部的视频占位内容
- 修正 CORS_SOLUTION.md 中的脚本路径引用,添加 `scripts/` 前缀使其与实际文件结构一致
- 清理 SECURITY.md 中重复的 CSP 说明,精简为单条以 server.js 为基准的说明;在通知系统文档顶部增加快速摘要说明;在 CLAUDE.md 中补充缺失的 `cors.js` 相关文件引用
2026-06-02 00:03:12 +08:00
Abner
b2c84457e6 ♻️ refactor: 增强异步请求生命周期管理与页面离开守卫机制
- 引入 `activeRequests` 集合追踪进行中的异步请求(二维码生成、安装确认),并在用户尝试导航时检测拦截,防止意外离开导致激活流程中断
- 添加 `beforeunload` 页面关闭/刷新守卫,在请求进行中或处于关键步骤(Step 3/4)时触发浏览器原生确认弹窗,完成二维码生成后自动豁免拦截
- 在 `handleGenerateQR` 和 `handleConfirmInstall` 中使用 requestId 标记请求生命周期,导航守卫确认后丢弃过期请求的结果与错误,避免状态污染
- 重构 `generateQRCode` 函数,引入单服务 5 秒超时定时器和 `_qrGeneration` 计数器,防止并发调用时旧回调覆盖新超时警告,回退到备用服务商时重置计时器
- 分离二维码超时为独立翻译键 `qr.timeout` 与生成失败 `qr.failed`,补充中英双语翻译,并新增进行中请求警告的提示文案
2026-06-01 23:00:20 +08:00
Abner
4e0031b599 ♻️ refactor: 统一二维码超时/失败文案的 i18n key 与使用方式
- 将 Giffgaff 二维码生成超时/失败提示从静态字面量切换为 i18n key:giffgaff.app.qr.timeout 与 giffgaff.app.qr.failed
- 同步更新 i18n 数据中二维码相关文案 key 的中英文翻译,删除旧的重复字面量条目
- Simyo 二维码生成失败回退提示改为使用 simyo.app.qr.failed 的 i18n key,保持提示内容一致性
- 移除 giffgaff 端关于“安全:tl() 返回静态字符串”的注释,直接改为使用 t() 取得翻译文本
2026-06-01 22:40:23 +08:00
Abner
61d1265e4c Merge pull request #67 from Silentely/fix/issue-66-esim-token-error-handling
🐛 fix(giffgaff): eSIM Token 获取失败时的状态脱节修复 (issue #66)
2026-06-01 22:10:26 +08:00
Abner
29e6913ae9 🐛 fix(giffgaff): 修复 PR 审查发现的 3 个问题
- ui-controller: tl() 改为 t() 用于命名空间键,确保翻译正确显示
- ui-controller: 导入 t 函数
- ui-controller: generateQRCode 添加 generation counter 防止并发竞态
2026-06-01 21:51:15 +08:00
Abner
1af723d92a 🐛 fix(giffgaff): lpaString 状态写入延迟到 requestId 校验之后
- esim-service.getESimDownloadToken 移除 stateManager.set('lpaString')
- handleGetToken 在 requestId 检查通过后才写入 lpaString
- smsActivateFlow 补充 lpaString 状态写入
- 防止 stale 请求污染 session 形成隐藏完成状态
2026-06-01 21:22:51 +08:00
Abner
09647324c5 🐛 fix(giffgaff): beforeunload 豁免仅在 Step 5 生效,离开时自动重置
- stateManager.subscribe 中监听步骤变化,离开 Step 5 时重置 _beforeunloadExempt
- 确保后续关键步骤仍受 beforeunload 拦截保护
2026-06-01 21:18:04 +08:00
Abner
53c734a900 🐛 fix(giffgaff): 修复 PR #67 代码审查发现的 13 个问题
- sentry-loader: 修复 exceptionValue 未定义导致 beforeSend 崩溃
- esim-service: _tryParseError 兼容 GraphQL errors 数组格式,_friendlyGraphqlError 回退到 parsed.error
- giffgaff-app: 移除未使用 result 变量;await 后检查 requestId 防止过期请求执行 UI 更新
- giffgaff-app: Math.min 降级直接使用 targetStep;showToast 移入 if(error) 块
- giffgaff-app: beforeunload 添加 _beforeunloadExempt 豁免标志;清除会话时重置
- ui-controller: QR 超时提取 startTimeout 辅助函数消除重复;添加 innerHTML 安全注释
- i18n-data: 统一使用命名空间键,移除冗余字面量翻译
- sentry-loader: /swal/i 正则收窄为精确匹配
2026-06-01 21:08:50 +08:00
Abner
bec1937f7e 🐛 fix(giffgaff): eSIM Token 获取失败时的状态脱节修复 (issue #66)
- 新增 activeRequests Set 追踪活跃请求,防止请求进行中误操作
- handleGetToken() 增加 SSN/accessToken 前置校验,防止 422/401
- bindStepNavigation() 添加导航守卫,请求中拦截步骤切换
- 新增 beforeunload 守卫,拦截页面刷新/关闭
- handleSessionRestore() 状态一致性降级,Math.min 防止降级失效
- getESimDownloadToken() 错误附加 status code 便于分类处理
- Step 4/5 添加刷新警告提示
- 二维码超时处理:单服务 5 秒超时,回退时重置计时器
- 替换已废弃 chart.googleapis.com 为 api.qrserver.com
- 新增中英文翻译 key 并同步 LITERAL_TRANSLATIONS
2026-06-01 20:32:50 +08:00
Abner
11fe7f79dd ♻️ refactor: 简化全局错误处理逻辑,将错误上报职责完全移交 Sentry
- 移除 `setupGlobalErrorHandlers` 中手动调用 `Sentry.captureException` 的逻辑,避免与 Sentry 自动采集产生重复上报
- 删除 `isSentryIgnoredError` 方法,相关噪音过滤统一由 Sentry 内置 `ignoreErrors` 配置处理
- 在 Sentry 配置(`sentry-init.js` 和 `sentry-loader.js`)的 `beforeSend` 中新增对非 Error 类型 rejection 对象的过滤,防止 API 错误响应被当作代码 bug 上报
- 将 `swal` 关键词加入 Sentry `ignoreErrors` 列表,过滤浏览器扩展注入 SweetAlert 冲突产生的噪音
- 为 `setupGlobalErrorHandlers` 添加职责边界注释,明确本 handler 仅负责噪音检测和用户通知
2026-06-01 19:29:28 +08:00
Abner
7f4abfeda9 🐛 fix: eSIM 请求失败时提供友好错误提示并补充 Cookie 透传
- 在 esim-service 的关键 GraphQL 请求中补充 cookie: stateManager.getCookie(),避免上游鉴权因缺失 cookie 导致失败
- 新增 _tryParseError 与 _friendlyGraphqlError:解析 JSON 错误体并按状态码区分 401(token 过期)、422(SSN 不存在/业务校验失败)与其他错误,返回对应中文文案
- 更新 i18n-data 增加 tokenExpired、authFailed、ssnNotFound、upstreamBusinessError 等多条 i18n 错误文案,供友好提示使用
- 调整 giffgaff-app 在 eSIM Token 获取失败时直接展示 esim-service 返回的错误 message,避免被旧的通用错误文案覆盖
- 在 mfa-handler 的 simSwapMfaChallenge 请求中补充 cookie 字段,确保 MFA 相关上游调用的鉴权上下文一致
2026-06-01 19:14:58 +08:00
Abner
4ba791b660 🔧 chore: 优化 Cookie 验证分支逻辑并补充跨文件 CORS 注释
- 重构 `cookieHandler.checkResult` 的条件分支,将"验证通过但未拿到 JWT"的场景从模糊的 `!result.valid` 升级为更精确的 `result.success && !result.valid`,使三类结果路径语义更清晰
- 为三个分支各自添加中文注释,分别标注"完全成功"、"部分成功"和"硬错误,不可继续",提升代码可维护性
- 在 `bff-proxy.js` 顶部新增注释说明该文件运行在 Deno 环境,无法直接引用 Node.js 的 `_shared/cors.js`,并提醒需与 `netlify/functions/_shared/cors.js` 的 `parseOrigins` 保持同步
2026-05-31 21:46:01 +08:00
Abner
33b8e20440 ♻️ refactor: 抽取内部请求头构建并规范 Cookie 验证返回语义
- 新增共享模块 netlify/functions/_shared/internal-headers.js,集中构建内部函数互调请求头,统一 ACCESS_KEY 未配置时的 500 抛错逻辑
- 更新 giffgaff-graphql.js / giffgaff-mfa-challenge.js / giffgaff-mfa-validation.js,移除各自重复的 getInternalHeaders 实现,改为复用 getInternalHeaders
- 调整 verify-cookie.js 返回语义:以 success 表示 HTTP 层请求是否成功、以 valid 表示是否具备可用于后续 API 的 JWT 令牌
- 修正 verify-cookie.js 错误提示文案中的乱码问题,确保提示“无法通过Cookie获取访问令牌”表述正确
2026-05-31 20:30:31 +08:00
Abner
df2b6ca814 🐛 fix(auth): 收紧 BFF 鉴权并修复内部调用链路 2026-05-31 19:25:23 +08:00
Abner
b815225845 🐛 fix(auth): 收紧 BFF 鉴权并保持内部路由兼容 2026-05-31 18:15:45 +08:00
Abner
72f76bcc79 feat: 更新已付款直取 eSIM 文案与界面提示
- 调整 UI 状态模式文案,将“已购用户获取 eSIM(仅限新购)”替换为“已付款用户直取 eSIM”,并同步状态样式入口
- 更新 i18n 翻译数据:将关键翻译键从“已购用户获取eSIM(仅限新购)”替换为“已付款用户直取 eSIM”,保持英文等值适配
- 完善模块化页面提示信息:在新用户告知与功能确认列表中新增“已支付 £10 直取 eSIM”,并更新直取卡片说明与徽标为“已付款用户专用”
- 更新直取 eSIM 内联区域说明语义:从“新购用户”修正为“已付款用户”,明确条件为“£10 已付但未成功生成 eSIM”
2026-05-30 21:18:07 +08:00
Abner
132d1ac0dd Merge pull request #64 from Silentely/dependabot/npm_and_yarn/axios-1.16.0
build(deps): bump axios from 1.15.2 to 1.16.0
2026-05-30 11:33:15 +08:00
dependabot[bot]
31098270d2 build(deps): bump axios from 1.15.2 to 1.16.0
Bumps [axios](https://github.com/axios/axios) from 1.15.2 to 1.16.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.15.2...v1.16.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.16.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-29 16:23:07 +00:00
Abner
0a799f1b30 Merge pull request #63 from Silentely/feat/direct-fetch-esim
 feat: 新增「已支付直取 eSIM」功能
2026-05-29 18:52:31 +08:00
Abner
6631feb817 📝 docs: 更新警告提示文案并补充多 eSIM 选择说明
- 将 `⚠` 警告符号统一替换为 `⚠️`(含 Emoji 编码),确保中英文页面和文档的警告样式一致
- 中文文档和 HTML 中更新警告文案为"作者因条件不符未能实测此功能",英文对应更新为"could not be fully tested by the author due to eligibility constraints",表述更准确
- 在中文和英文用户指南中补充第 4 步操作说明:当账户存在多个可下载的 eSIM 时,需从列表中选择需要的 eSIM 后点击确认
- 将文档中 `reserve/swap` 流程替换为带代码格式的 `reserveESim` / `swapSim`,更精确反映实际接口名称
- 将中文文档中按钮名称的英文引号(「」)改为中文引号(""),保持中文排版风格统一
2026-05-29 18:51:05 +08:00