Commit Graph

16 Commits

Author SHA1 Message Date
Abner
81b86c7ec3 test: 补充 Logger Release 与 env 输出用例 2026-07-17 21:02:07 +08:00
Abner
2eda195a89 test: 补充诊断导出与帮助弹窗防护用例
同步更新 AuthHandler 断言与模块文档说明。
2026-07-17 20:53:32 +08:00
Abner
890e05f27f test: 补充 OAuth、登录、剪贴板与反馈相关单测
修复 sentry-feedback 与 qrcode 断言,新增 clipboard/PKCE/OAuth 回调/AuthHandler/showToast 覆盖。
2026-07-17 20:49:10 +08:00
Abner
68e931b901 ♻️ refactor: 将日志输出格式从 JSON 重构为人类可读的字符串
- 修改 `server-logger.js` 核心逻辑,将日志输出从 `JSON.stringify` 改为模板字符串拼接,格式变更为 `[LEVEL] [function] [requestId] message | key=value`
- 同步更新 `bff-proxy.js` 和 `markdown-negotiation.js` 中的 Deno 环境内联日志函数,保持 Edge Function 与 Node.js 环境日志格式一致
- 调整 context 序列化方式,使用 `key=value` 空格分隔,无 context 时不输出分隔符 `|`,并移除 `timestamp` 字段
- 更新 `server-logger.test.js` 测试用例,断言日志输出为字符串匹配而非 JSON 解析,并增加单行输出及无 context 时不包含分隔符的验证
2026-06-26 21:48:03 +08:00
Abner
574506bcbf feat: 添加结构化日志支持并优化错误处理
- 在 netlify/functions/_shared/server-logger.js 中重构日志函数,统一日志输出格式,支持 context 字段覆盖基础字段(如 message)
- 在 netlify/functions/_shared/middleware.js 中增强 withAuth 中间件,注入结构化 logger 到上下文,增加 request_start / request_end / request_error 日志追踪,并记录请求耗时
- 为所有 Netlify Function(giffgaff-sms-activate、giffgaff-graphql、auto-activate-esim、verify-cookie、giffgaff-token-exchange、giffgaff-mfa-challenge、giffgaff-mfa-validation、health)添加结构化日志,替换原有 console 输出,包含请求入参、状态和耗时
- 在 netlify/edge-functions/bff-proxy.js 和 markdown-negotiation.js 中实现 Deno 内联结构化日志,生成 requestId 并统一日志输出格式,支持 INFO/WARN/ERROR/DEBUG 级别控制
- 更新测试用例 tests/modules/server-logger.test.js 和 tests/functions/middleware-logging.test.js,新增日志级别过滤、context 覆盖、多实例隔离、OPTIONS 预检处理等测试场景
2026-06-26 21:28:21 +08:00
Abner
89bacd46ed feat: 新增服务端结构化日志模块并完善日志相关测试
- 新增 `server-logger` 工具,输出单行 JSON 日志,统一包含 level、message、function、requestId、timestamp 等字段并支持 context 扩展
- 实现日志级别控制(`LOG_LEVEL` 环境变量),通过 `createLogger` 工厂按需抑制 DEBUG 日志并提供 `parseLogLevel/LOG_LEVELS` 供测试验证
- 新增中间件集成测试 `middleware-logging.test.js`,验证请求开始/结束/异常日志、耗时统计、logger 注入以及每次请求生成唯一 requestId
- 新增模块单元测试 `server-logger.test.js`,覆盖 JSON 结构、context 合并、warn/error 路由到对应 console 方法、以及 DEBUG 级别在不同环境下的输出行为
2026-06-26 21:22:26 +08:00
Neo Vern
7298ae4a0d feat: QR 码本地打包 + Edge Function 直接生成 (#86)
*  feat: 打包 qrcode-generator@1.4.4 到本地 ES 模块

* ♻️ refactor(qrcode): 移除 CDN 加载逻辑,改用本地 import 引入 qrcode-generator

* 🔧 chore: 移除 CDN preconnect 提示,QR 码库已内联

* 🔧 chore: 清理 CSP 配置,移除不再需要的 CDN 域名

*  feat: 将 QR 码生成迁移到 Edge Function,消除后端冷启动延迟

- 在 Edge Function 中内联 qrcode-generator 库(~20KB),直接生成 QR 码
- 删除废弃的 Netlify Function (qrcode-generate.js)
- 更新 server.js 移除对已删除函数的引用
- 更新测试文件适配新的 Edge 内联架构

* ♻️ refactor: 代码质量修复 — 移除死代码、消除变量遮蔽、添加交叉引用注释

* 🔧 chore: 修复非阻塞风险 — 补充 strict 模式、移除废弃 qrcode 依赖

- Edge Function qrcode-lib.js 补充 'use strict' 声明,与浏览器版保持一致
- 移除已废弃的 qrcode npm 依赖(原用于已删除的 Netlify Function)

* ♻️ refactor: 消除魔法数字、补充脆耦合和 async 技术债注释

- Edge Function 中 QR margin 魔法数字 8 替换为 QR_MARGIN_MODULES 常量
- generateQRCodeLocal 补充 async 无 await 的技术债说明
- error.message.startsWith 条件补充校验函数耦合关系注释

* 🐛 fix: 修复边界条件 — null JSON body、vendor 字符串异常、CDN preconnect 残留

- Edge Function: null JSON body 解构移到 try/catch 内,添加 null/非对象检查
- qrcode-generator: 库 throw 字符串时统一转换为 Error 对象,避免 .startsWith 崩溃
- index.html: 移除不再需要的 CDN preconnect 提示(jsdelivr/cdnjs)

* ♻️ refactor: 修复 CSP 恢复、异常归一化、自定义 Error 类、负面路径测试

- netlify.toml: 恢复 script-src 中 cdn.jsdelivr.net(Bootstrap JS 仍依赖)
- Edge Function catch: 归一化非 Error 异常(库可能 throw 字符串)
- Edge Function QR margin: createDataURL(cellSize, 2) → createDataURL(cellSize, cellSize * 2) 对齐模块边距
- Edge Function: 补充认证模型和 QR 格式变更注释
- qrcode-generator.js: 引入 QRCodeValidationError 替代 error.message.startsWith 脆耦合
- tests/bff-proxy: 添加 6 个 Edge QR 负面路径测试(无效 JSON、空/超长/非字符串 data、超范围 size)
- tests/qrcode-generator: 添加库 throw 字符串异常的测试
- scripts/sync-qrcode-lib.js: 新增库代码同步验证脚本

* 🐛 fix: 修复 server.js CSP 缺少 jsdelivr、同步脚本 CRLF 归一化、fallback 拦截校验错误

- server.js: 恢复 script-src 中 cdn.jsdelivr.net(本地开发 Bootstrap JS 依赖)
- sync-qrcode-lib.js: 归一化 CRLF 换行符避免跨平台误报
- qrcode-generator.js: generateQRCodeWithFallback 入口处拦截 QRCodeValidationError,避免无效输入触发无意义的后端降级
2026-06-25 22:15:17 +08:00
Abner
7221eb2e49 🐛 fix(qrcode): 添加 loadQRCodeLibrary 返回值防御性验证
解决 ESIM-TOOLS-15:浏览器扩展污染或缓存竞态导致 qrCodeLib 不是函数的极端情况。
新增重试机制:清除缓存后重新加载 CDN,避免用户看到 QR 码生成失败。
2026-06-24 23:03:30 +08:00
Neo Vern
bb80d3dc0a feat(sentry): 添加用户反馈功能 (#83)
*  feat(sentry): 添加用户反馈功能

- 升级 CDN bundle 以支持 feedback 模块
- 添加 feedbackIntegration 内置 Widget(主动反馈)
- 实现错误后自动弹窗(Crash-Report Modal)
- 添加冷却机制防止频繁弹窗(60秒冷却 + 同一错误不重复)

* ♻️ refactor(sentry): 修复审查发现的问题

- 消除指纹计算重复(DRY 违反)
- 统一使用 const/let 替代 var
- 复用已有 showReportDialog 函数
- 添加 beforeSend UI 副作用设计说明注释

* 🐛 fix(sentry): 修复 SentryMock 缺失方法

- 添加 feedbackIntegration 和 replayIntegration 到 SentryMock
- 修复 CDN 加载失败时 TypeError
- 删除多余空行

* 🐛 fix(sentry): 修复代码质量问题

- sentry-loader.js 添加 replayIntegration 和 feedbackIntegration 配置
- sentry-loader.js 添加错误后自动弹窗逻辑(冷却机制 + try-catch)
- sentry-init.js 添加 showReportDialog try-catch 保护
- 弹窗延迟从 100ms 改为 500ms(弱网兼容)

*  feat(sentry): 修复所有代码质量问题

- H1: CSP 添加 *.sentry.io 到 script-src(4 个文件)
- M1: Feedback Widget 和自动弹窗添加中文文案
- M2: 同步两套初始化路径配置(tracing、脱敏、ignoreErrors)
- M3: sentry-entry.js 添加 feedbackIntegration 导出
- M4: 新增专项测试(13 个测试用例)
- L1: SentryMock 补齐 showReportDialog/lastEventId
- L2: 空事件对象防御

*  feat(sentry): 修复 PR 审查发现的全部问题

功能正确性:
- query_string 添加类型检查(字符串/对象兼容)
- 指纹计算移到脱敏前(避免不同错误被误判为重复)
- 冷却状态更新移到 setTimeout 内部(避免空转)
- subtitleLine2 改为 subtitle2(修正字段名)

CSP 修复:
- script-src 移除 *.sentry.io(减少攻击面)
- frame-src 添加 *.sentry.io(支持弹窗 iframe)
- 添加 worker-src/child-src blob:(支持 Session Replay)
- server.js helmet CSP 同步更新
- netlify.toml 注释与实际策略对齐

配置/架构:
- sentry-entry.js 添加 lastEventId 导出
- tracePropagationTargets 添加 CORS 风险注释
- CDN 架构添加说明注释

代码质量:
- URL 参数脱敏支持大小写不敏感
- 移除过宽的 ignoreErrors 规则
- 测试文件添加 use strict
- 添加 i18n 和漂移风险注释

* fix: apply CodeRabbit auto-fixes (#85)

Fixed 3 file(s) based on 5 unresolved review comments.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: CodeRabbit <noreply@coderabbit.ai>

*  feat(sentry): 修复新一轮审查意见

CSP 修复:
- script-src 恢复 *.sentry.io(showReportDialog 需要)
- netlify.toml Header CSP 同步 Cloudflare/Google
- server.js helmet CSP 同步

冷却竞态修复:
- 状态预留移到 setTimeout 之前(防止 500ms 内重复弹窗)
- 弹窗失败时回滚冷却状态

脱敏修复:
- query_string 对象分支改为大小写不敏感
- sanitizeQueryString 迭代改为安全模式(先收集键再遍历)

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
2026-06-24 19:04:38 +08:00
Abner
6379bbef90 ♻️ refactor: 移除外部二维码服务依赖,改用本地生成方案
- 将前端二维码生成从 `qrcode.show` 远程服务切换为 `qrcode-generator` 本地库(UMD 格式),消除对外部 CDN 渲染服务的依赖,降低隐私风险和第三方服务不可用时的故障点
- 重写 `generateQRCodeLocal` 函数适配 `qrcode-generator` API,使用 `qrcode(typeNumber, errorCorrectionLevel).addData().make().createDataURL()` 模式,并基于 QR 码模块数动态计算 `cellSize`,新增大尺寸预览图支持(400px)
- 移除所有页面中的 `qrcode.show` 域名引用,包括 CSP `connectSrc` 策略、preconnect 标签、resource-hints DNS 预解析配置及 Simyo/Giffgaff API 配置中的 `qrcode` 端点
- 完善二维码生成的日志与监控:在 CDN 加载成功后输出 `console.log`,本地/后端生成成功及失败时分别输出对应级别日志,并在 `trackQRCodeEvent` 中增加 `isBrowser` 守卫避免非浏览器环境报错
- 增加生成耗时和 QR 码长度(不含内容)到后端 BFF 日志,同时统一使用 `Date.now()` 计算请求耗时,避免 LPA 激活码等敏感信息进入日志
- 全面更新所有相关测试用例,适配新的 `window.qrcode` 工厂函数 mock,新增日志输出和 Sentry 上报断言,验证本地成功、本地失败降级、后端成功、后端失败四种场景的可观测性
2026-06-23 21:22:16 +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
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
df2b6ca814 🐛 fix(auth): 收紧 BFF 鉴权并修复内部调用链路 2026-05-31 19:25:23 +08:00
Abner
b5ec8eb18a 🐛 fix: 修复受限环境下 secure-storage 触发 sessionStorage SecurityError
- 新增 safeGetStorage 探测能力探针,捕获 sessionStorage/localStorage 读取与写入探测时抛出的 SecurityError,返回可用的 Storage 或 null
- 将顶层敏感数据迁移逻辑外层包裹 try-catch,避免 iframe/sandbox/隐私设置等受限上下文导致模块初始化阶段未捕获异常中断
- 在 migrateFromLocalStorage 中基于 safeGetStorage 动态获取 localStorage,localStorage 不可用时直接跳过迁移而非抛错
- 补充受限环境降级单元测试:模拟 sessionStorage/localStorage 属性读取被拒、以及 sessionStorage 写入失败时确保降级到 fallbackStorage 且不抛异常
2026-05-08 18:33:59 +08:00
Abner
9ed51287c0 test: 新增多个核心模块的单元测试并优化 Jest 配置
- 在 `jest.config.js` 中启用 V8 覆盖率提供器(兼容 Node.js 22+),提升覆盖率检测性能与准确性
- 调整覆盖率收集范围为 `src/js/modules/**/*.js`,排除测试文件,使报告更聚焦于实际业务逻辑
- 降低全局覆盖率阈值至当前可达成水平(statements/branches 30%-70%),制定逐步提升计划
- 新增 6 个模块的完整单元测试:`APIService`、`AppConfig`、`HTMLSanitizer`、`Logger`、`SecureStorage` 和 `utils`,总计约 840 行测试代码
- 修复 `HTMLSanitizer.sanitizeURL()` 中对 `data:text/html` 协议的正则检测逻辑,增强 XSS 防护能力
2026-05-05 10:35:14 +08:00