Repository navigation
Development
推荐使用发布 CI 采用的 Node.js LTS;扩展的验证环境见扩展 CI。使用 npm ci 按锁文件安装依赖,所用 Node.js 版本须满足锁定依赖的运行要求。
需要 Git 和 npm。仅构建、单元测试不需要生产 Cloudflare 凭据;部署或使用 Cloudflare 远程资源时才需要账户授权。不要把日常测试指向保存真实 2FA 账户的生产 KV。
git clone https://github.com/wuzf/2fa.git
cd 2fa
npm ci
npm run dev按 Wrangler 输出打开本地地址。npm run dev 与 npm start 都使用 wrangler dev --env development。首次进入本地实例仍需要在 /setup 设置密码。需要测试加密或 OAuth 时,使用本地开发配置和专用凭证,并遵循配置说明。
仓库使用 ES Modules、原生 Web API 和 Web Crypto。页面脚本大量由模板函数生成,新增浏览器全局依赖、序列化函数或嵌套模板字符串时,应检查最终 Worker 构建和浏览器结果。
| 命令 | 用途 |
|---|---|
npm ci |
严格按锁文件安装依赖,并运行包括 Husky prepare 在内的正常安装脚本 |
npm run dev / npm start
|
Wrangler development 本地开发 |
npm test -- --run |
执行一次完整 Vitest 测试 |
npm test / npm run test:watch
|
Vitest 默认交互 / 监听开发流程 |
npm run test:ui |
Vitest UI |
npm run test:coverage -- --run |
生成一次覆盖率报告 |
npm run lint |
严格 ESLint,警告也失败;覆盖 src、extension/src、tests、scripts |
npm run lint:fix |
对上述范围运行可自动修复的 ESLint 规则 |
npm run format:check / npm run format
|
检查 / 格式化 src/**/*.js;并非全仓文件 |
npm run build |
构建 Worker 发布文件 |
npm run build:minify |
构建压缩 Worker |
npm run build:extension |
构建 Chrome / Edge 扩展目录 |
npm run package:extension |
构建 Chrome / Edge / Firefox 并生成 ZIP |
npm run icons:extension |
重新生成扩展图标 |
npm run test:extension |
扩展 Vitest 测试 |
npm run test:extension:coverage |
扩展专用覆盖率检查 |
npm run test:extension:e2e |
构建 Chrome / Edge 扩展并运行浏览器端到端测试 |
npm run release:sync |
同步现有应用版本到登记文件,不创建发布提交或 tag |
npm run deploy、deploy:git、deploy:package 和 deploy:dev 会实际部署;deploy:direct 当前也是部署脚本的别名,不能当作本地构建或 dry-run。只有准备更新对应环境时才使用,详见部署指南。
常规改动至少按影响范围执行测试;提交前执行完整必要检查:
npm run lint
npm test -- --run
npm run buildnpm ci 安装的 Husky 钩子在提交时生效:pre-commit 对暂存文件运行 lint-staged,src、extension/src、tests、scripts 下的 JS 执行 ESLint 自动修复(警告也失败),src、extension/src 的 JS 再经 Prettier 格式化,Markdown 只经 Prettier 格式化;commit-msg 要求提交信息使用 Conventional Commits 格式 <type>(<scope>): <subject>。
Vitest 配置使用 Node 环境,通过 tests/setup.js 补齐 Web Crypto 等 API。KV、Durable Object 和 HTTP 行为有对应测试替身;tests/storage/、tests/api/、tests/ui/、tests/extension/、tests/scripts/ 等覆盖不同层。针对某个模块可运行:
npx vitest run tests/storage/secrets-store.test.js
npx vitest run tests/storage/consistent-kv.test.js导入相关修改还应验证实际导出 → 页面预览 → 批量 API → 存储的往返,参见 tests/ui/json-import-roundtrip.test.js。本项目 JSON 的解析器直接返回独立字段,其他验证器格式和原生 OTPAuth 仍走 URI 解析;不要仅测试中间 URI 就认定空账户、含冒号的服务名和 HOTP 计数器得以保留。预览前被解析器跳过的条目也不能当作已出现在预览统计中。
部署配置合并的回归测试位于 tests/scripts/merge-wrangler-config.test.js 和迁移相关测试中,覆盖带注释或引号的表头、多行变量、字符串中的伪配置文本,以及按实际 binding 识别 KV 表。scripts/merge-migrations.js 提供 TOML 表头和完整赋值定位,merge-wrangler-config.js 按位置替换受支持的配置;语法识别完善不意味着保留任意自定义环境、额外绑定或源码,保留范围见升级与回滚。
单元测试通过不能替代 Cloudflare 上的实际集成验证:尤其是 Durable Object 绑定、KV 最终一致性、OAuth 回调、Cron、浏览器 Cookie 和扩展权限。需要远程验证时,使用独立 Worker、KV 和测试账户;显式区分 development 与 production,不复用生产命名空间。测试页面、导出文件和截图只放虚构账户及测试密钥。
网页的离线变更队列有独立浏览器测试入口:
npx playwright install chromium
npx playwright test --config tests/e2e/offline-queue.config.js测试自动启动本地 HTTP 测试服务,使用项目实际页面脚本、Service Worker 和浏览器 IndexedDB,后端以虚构账户模拟登录失效、网络与写入错误等场景,不需要连接生产 Worker。启动时优先使用已发现的本机 Chrome,否则使用 Playwright Chromium;结果保存到 test-results/web-offline/。这是网页测试,与下面的扩展测试分别执行。
首次运行需安装 Playwright Chromium:
npx playwright install chromium
npm run test:extension:e2eLinux CI 使用 npx playwright install --with-deps chromium 安装系统依赖。测试运行单个 worker,使用持久浏览器上下文加载扩展;输出位于 test-results/extension/,CI 另生成 HTML 报告。默认还包含已安装 Chrome / Edge 的冒烟测试;只使用固定版本 Playwright Chromium 验证两个扩展构建时,设置 EXTENSION_E2E_SKIP_BRANDED=1。
PowerShell 示例:
$env:EXTENSION_E2E_SKIP_BRANDED = '1'
npm run test:extension:e2e
Remove-Item Env:EXTENSION_E2E_SKIP_BRANDEDBash 示例:
EXTENSION_E2E_SKIP_BRANDED=1 npm run test:extension:e2eFirefox 包由打包命令生成;上述端到端命令主要验证 Chrome / Edge,不代表 Firefox 已完成同等浏览器集成覆盖。界面或权限改动应另行在对应目标浏览器检查。用户手动安装步骤见浏览器扩展。
Android 开发调试使用电脑连接设备、Android Platform Tools、USB 调试和 Firefox 的 USB 远程调试。构建 dist/extension/firefox 后可通过 web-ext run --target=firefox-android 临时加载;设备 ID、Firefox 应用包名等参数应按实际设备设置,详见仓库扩展开发与测试。临时调试安装不等于商店签名安装。
已验证的版本、设备及未覆盖场景见Android 扩展验证记录。复用记录时应保留其证据范围,不应推断未覆盖的版本、设备或安装方式已经验证,也不要将桌面浏览器的小视口测试当作 Android 实机验证。
npm run build 用 esbuild 将 src/worker.js 打包为 ES Module,目标 es2022,生成:
-
dist/worker.js:单文件 Worker,保留 Durable Object 导出。 -
dist/worker.metadata.json:版本、构建时间、大小等元数据。 -
dist/DEPLOY.md:本次构建的部署说明。
构建还嵌入 Service Worker 版本回退值,以便 Dashboard 单文件部署也能更新 PWA 缓存。只复制 JS 文件不会自动创建 KV、Durable Object 或迁移配置,实际部署仍须检查绑定。
npm run build:extension 生成 dist/extension/chrome/ 和 dist/extension/edge/。npm run package:extension 另外构建 Firefox,输出 dist/2fa-extension-{browser}-{version}.zip;清单位于 ZIP 根目录。Chrome / Edge 构建会清理扩展输出目录,因此不要在那里手工保存需要保留的文件。
扩展与主程序独立编号。扩展版本源是 extension/manifest.base.json,不是根目录 package.json,不同版本号是正常设计。
普通开发不必执行发布命令。维护者发版时:
- 准备
docs/releases/vX.Y.Z.md,确保功能修改已提交,版本相关文件与发布工作区符合脚本要求。 - 运行
npm run release:patch、release:minor、release:major,或node scripts/release.js X.Y.Z。 - 脚本先执行 lint、全量测试、Worker 构建和扩展打包;失败在改版本、提交、建 tag 之前停止。
- 通过后更新根
package.json/package-lock.json,同步src/utils/version.js以及中英文 README 版本徽章,执行版本一致性自检,然后创建版本提交和对应 tag。 - 核对提交和 tag 后,按脚本提示用
git push --atomic origin HEAD vX.Y.Z同时推送当前分支和本次发布 tag(替换为实际版本)。只推 tag 不会更新远程分支。GitHubPublish release工作流会再次安装、检查、构建、打包并发布 GitHub Release 附件和说明。
发布工作流只接受 vX.Y.Z 形式的稳定版本 tag,使用 npm ci --ignore-scripts 安装依赖,随后执行与发布脚本一致的检查。GitHub Release 发布和 Cloudflare Worker 部署是两件事;用户或维护者仍需按部署流程更新 Worker。扩展商店上架同样不由这一步自动完成。
修改版本号时不要只改页面显示。需要同步而不发版可使用 npm run release:sync;不要用它替代正常版本检查。保持扩展自己的清单版本与发布包一致,扩展有变更需要单独判断是否递增。
| 改动类型 | 需要连带核查 |
|---|---|
| API / 认证 | 真实路由、参数 schema、HTTP 状态、Cookie / Bearer、语言头、CORS、API 参考 |
| 账户写入 / HOTP | 串行存储、KV 旧值、sidecar 合并、409 / 503 处理、备份、离线重放和旧版回滚 |
| 导入导出 / 恢复 | TOTP / HOTP 参数不丢失、非标准旧数据、预览与错误提示、格式互转、全库覆盖提示 |
| UI 文案 / 语言 | 各语言字典、独立页面、离线消息、API 诊断、备份文档和扩展 |
| 视图 / 校时 | 默认视图与临时切换、缓存首次显示前恢复排序、同语言不重绘、后台校时后更新验证码 |
| 生成器 / 构建 | 普通及压缩构建、生成脚本中的变量依赖、按需模块缓存、Service Worker 更新 |
| 扩展 | 三个目标清单、权限、来源授权、脱机数据、浏览器测试和隐私说明 |
不要因为一个主流程测试通过就删掉失败场景或兼容场景。测试应验证行为,例如并发修改不丢失、过期会话不暴露缓存、HOTP 冲突不重复推进,而不是简单复制实现。
提交问题前搜索现有 Issue,附可复现步骤、版本、部署方式、浏览器和已脱敏错误信息。不要公开真实 Base32 密钥、登录令牌、Cookie、云存储凭证或完整备份。安全问题按仓库安全政策报告。