{"id":30435509,"url":"https://github.com/lzhbhlrpython/card-wallet","last_synced_at":"2026-02-11T18:03:27.414Z","repository":{"id":309575596,"uuid":"1036605505","full_name":"lzhbhlrPython/card-wallet","owner":"lzhbhlrPython","description":"A card-info storage platform with TOTP","archived":false,"fork":false,"pushed_at":"2025-08-12T16:21:51.000Z","size":161,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-12T17:34:37.956Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Vue","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lzhbhlrPython.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-12T10:16:23.000Z","updated_at":"2025-08-12T16:21:05.000Z","dependencies_parsed_at":"2025-08-12T17:34:42.842Z","dependency_job_id":"829be615-189e-47ff-830d-4f75ac604878","html_url":"https://github.com/lzhbhlrPython/card-wallet","commit_stats":null,"previous_names":["lzhbhlrpython/card-wallet"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/lzhbhlrPython/card-wallet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lzhbhlrPython%2Fcard-wallet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lzhbhlrPython%2Fcard-wallet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lzhbhlrPython%2Fcard-wallet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lzhbhlrPython%2Fcard-wallet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lzhbhlrPython","download_url":"https://codeload.github.com/lzhbhlrPython/card-wallet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lzhbhlrPython%2Fcard-wallet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271723846,"owners_count":24809789,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-22T02:00:08.480Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-08-23T00:35:51.355Z","updated_at":"2026-02-11T18:03:27.397Z","avatar_url":"https://github.com/lzhbhlrPython.png","language":"Vue","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Card Wallet (Vue + Express + Encrypted SQLite)\n\n一个带本地加密、用户认证与双因素认证 (TOTP) 的信用卡/卡片信息管理示例应用。前端使用 Vue 3 + Vite + Pinia，后端使用 Express + SQLite，并提供基于 WebDAV 的手动远程备份能力。\n\n\u003e 当前版本：1.4.1\n\n## 功能特性\n- 用户注册 / 登录（JWT 会话）\n- 可选 TOTP 双因素认证（Speakeasy + 二维码）\n- **卡片管理**：卡号 / CVV / 有效期基于 AES-256-CBC 加密后存入 SQLite\n- **证件管理**：护照、身份证、出入境通行证、驾驶证等证件信息使用 RSA-2048 加密（私钥经 AES 加密存储）\n  - 支持签发日期和有效期记录（支持长期有效选项）\n  - 日历组件提供友好的日期选择体验\n- 自动识别多种卡组织 (Visa / Mastercard / Amex / UnionPay / Discover / JCB / Diners / Maestro / MIR / CHINA T-UNION / eCNY / 其它)\n- 卡片支持记录持卡人姓名（加密存储，仅详情可见）\n- 证件支持多语种姓名（本国语言 + 拉丁转写）、多种有效期显示格式\n- 服务端校验卡号（含 Luhn 校验，特例：T-Union / eCNY）\n- 列表仅展示脱敏信息（卡片末四位、证件掩码号码），详情接口需二次 2FA 验证\n- 支持备注 note 字段\n- WebDAV 手动备份数据库（时间戳命名）\n- FPS 账户管理：列表、详情 (2FA)、创建、编辑、删除、备注隐私保护\n- 清空信息（Purge）：主密码 + 2FA（如启用）一次性删除卡片与 FPS 账户\n- 银行 Logo 多级回退：优先加载 svg，失败尝试同名 png，再回退 fps.png\n- 自动登出：token invalid 时自动清除会话并跳转登录页\n- **Modal 全局提示组件**：统一的信息提示和确认对话框（替代系统 alert）\n\n## 最近更新（v1.4.0）\n\nv1.4.0 重点优化了证件管理的用户体验，并引入了开发者友好的测试功能：\n\n- **日历组件**：\n  - 新增统一的 Calendar.vue 日期选择组件，支持日期选择和\"长期\"选项。\n  - 支持快速选择年份和月份的下拉选择器，年份范围：当前年份±50年。\n  - 月份显示使用英文缩写（Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec）。\n  - 应用于证件管理中的签发日期和有效期输入。\n  - 支持三种日期格式（YMD/MDY/DMY）的实时切换和显示。\n\n- **证件管理增强**：\n  - 新增签发日期字段，完整记录证件从签发到到期的完整周期。\n  - 支持长期有效标识，适用于长期身份证等无明确到期日的证件。\n  - 证件号码掩码统一为 8 位字符（格式：XX****XX），确保列表显示一致性。\n  - 证件详情页面优化：采用渐变背景 + 白色浮出层设计，提高可读性。\n  - 信息展示顺序优化：类型 → 姓名 → 证件号 → 签发日期 → 有效期 → 签发地点 → 备注。\n\n- **Modal 全局提示组件**：\n  - 封装统一的 Modal.vue 组件和 useModal composable。\n  - 支持 info/success/warning/error 四种类型，可自定义标题和按钮文本。\n  - 提供 alert() 和 confirm() 便捷方法，未来将替换所有系统 alert 调用。\n\n- **密码安全增强**：\n  - 注册、登录、清空数据时，客户端使用 MD5 对密码进行哈希后再传输至后端，避免明文密码在网络传输中暴露。\n  - 后端存储 bcrypt(MD5(password))，实现双重哈希保护（网络拦截仅得到 MD5，数据库泄露仅得到 bcrypt）。\n\n- **改进的测试脚本**：\n  - 支持测试签发日期和长期证件（20% 概率生成长期证件）。\n\n  - 优化输出格式，更清晰地展示各项测试结果。\n\n- **证件管理**：\n  - 新增 `documents` 表，支持护照、身份证（ID卡）、出入境通行证、驾驶证等证件类型。\n  - 证件信息使用 RSA-2048 非对称加密，每个用户拥有独立的 RSA 密钥对。\n  - 私钥使用 AES-256-CBC 加密后存储在数据库中，公钥用于加密证件数据。\n  - 列表显示掩码证件号（首尾各2字符，中间星号）、持有人姓名、证件类型。\n  - 完整证件信息（包括证件号、有效期、签发地点、拉丁转写）需要 2FA 验证。\n  - 支持多语种姓名：本国语言 + 可选的拉丁字母转写。\n  - 有效期支持三种显示格式：年/月/日 (YMD)、月/日/年 (MDY)、日/月/年 (DMY)。\n  \n- **前端增强**：\n  - 新增 `/documents` 证件列表页面，支持按类型筛选和排序。\n  - 新增 `/documents/new` 证件创建页面和 `/documents/:id/edit` 编辑页面。\n  - 证件详情通过 2FA 弹窗验证后在模态框中显示。\n  - 导航栏新增\"证件\"入口。\n\n- **安全改进**：\n  - 新用户注册时自动生成 RSA 密钥对。\n  - 旧用户首次登录时自动补充 RSA 密钥对（无缝升级）。\n  - Token invalid 时自动登出并跳转登录页（已在 v1.2 实现，本次确认）。\n\n## v1.3.0 更新\n\n以下为 v1.3.0 中实现的重要变更，包含数据库迁移、后端验证与前端 UX 改进：\n\n- 后端：新增 `cards.card_type` 字段（English enum），可能值：`credit | debit | prepaid | transit | ecny_wallet_1..4`。\n  - 通过数据库迁移（PRAGMA table_info -\u003e ALTER TABLE）自动添加列以兼容旧数据库。\n  - API 在创建/更新时校验并强制网络特殊规则：\n    - `tunion` (CHINA T-UNION) 强制 `card_type='transit'`，并对有效期/显示做特殊处理。\n    - `ecny` (eCNY) 必须使用 `ecny_wallet_1..4` 之一；服务端会把 CVV/有效期调整为 000 / 12/99（按规则展示）。\n  - 列表接口 `/cards` 仍然只返回非敏感元数据（服务器计算 `last4`）；卡片详情现在通过列表内弹窗查看（需 2FA），不再提供独立的 `/cards/:id` 路由。\n\n- 前端：显示与交互改进\n  - 在卡片创建/编辑表单增加“类型”字段（使用统一 `BankSelect` 风格控件），UI 展示为中文标签（信用卡/借记卡/预付卡/公交卡/一类..四类钱包），但提交给服务端为 English enum（`cardType` 字段）。\n  - `BankSelect` 增加 `readonly` / `allowCreate` / `allowClear` 属性：当 `readonly` 为真时，组件不会基于输入进行过滤，且可通过 `allowCreate=false` 禁止用户创建任意值（用于卡类型选择以避免非法输入）。\n  - 卡片列表增加“类型”筛选与“按类型”排序，排序使用固定顺序：信用卡 / 借记卡 / 预付卡 / 公交卡 / 一类钱包 / 二类钱包 / 三类钱包 / 四类钱包。\n  - 卡片项(`CardItem`)将 `类型` 放在左侧，`有效期` 放在右侧并列展示，字体权重与颜色统一以便快速识别。\n  - 卡片详情通过列表内弹窗查看（需 2FA）；独立路由 `/cards/:id` 已移除以避免重复入口点。列表中的“详情”按钮会打开弹窗；删除操作仍使用 2FA 弹窗确认。\n    - 卡片详情弹窗已升级为更拟真的卡面视觉：基于卡片信息生成确定性渐变配色（近似流体/柏林噪声效果），并在详情中展示持卡人姓名（仅在详情可见）。\n\n- 测试与文档\n  - `server_test.py` 已更新以在创建测试卡时包含 `cardType` 字段并覆盖特殊网络规则。\n  - `.github/copilot-instructions.md` 已同步更新，包含后端约束与前端表单/显示规则说明。\n\n 数据库兼容性注意：\n - 本次发布新增 `cards.card_type` 字段（枚举），服务端在启动时会尝试通过迁移（`PRAGMA table_info` + `ALTER TABLE`）自动添加该列，但建议在升级前先备份 `server/data/database.sqlite` 以防万一。\n - 如果你使用旧版本客户端（\u003c v1.3.0），可能无法正确显示或提交 `cardType` 字段；请同时更新前端到 v1.3.0 以确保兼容性。\n - 若需回退或审计迁移，请先备份数据库，再参阅 `server/index.js` 中的迁移逻辑与变更记录。\n\n## 目录结构\n```\nassets/            # 通用静态资源（卡组织 svg 等）\nclient/            # 前端 (Vue 3 + Vite)\n  src/\n  index.html\nserver/            # 后端 (Express)\n  index.js\n  data/            # 运行期生成的 SQLite 数据库 (被 .gitignore 忽略)\n```\n\n## 功能概览\n- FPS 账户：后端 `fps_accounts` 表与 `/fps` 相关 REST 接口（列表不含 note，详情/修改/删除需 2FA）。\n- 前端页面：`/fps` 列表、`/fps/new` 创建、`/fps/:id` 详情、`/fps/:id/edit` 编辑。\n- 备注 (note) 仅在 2FA 验证后可见；列表不展示以降低敏感泄漏面。\n- 银行 Logo 规则：银行字符串转大写，非字母数字替换为下划线，尝试加载对应 svg；缺失时使用 `fps.png` 兜底。\n\n## 快速开始\n### 1. 克隆与安装依赖\n```bash\ngit clone git@github.com:lzhbhlrPython/card-wallet.git card_wallet\ncd card_wallet\n# 安装前端\ncd client \u0026\u0026 npm install \u0026\u0026 cd ..\n# 安装后端\ncd server \u0026\u0026 npm install \u0026\u0026 cd ..\n```\n\n### 2. 配置环境变量\n在根目录或 `server/` 下创建 `.env`（不会提交，已在 .gitignore 中），并提供安全随机值。\n\n可用示例（请勿在生产使用示例值）：\n```\n# server/.env\nPORT=3000\nJWT_SECRET=change_this_to_long_random_string\nENCRYPTION_KEY=another_long_random_string_for_aes\n```\n如果你希望前端在构建中访问变量，可使用 Vite 约定：`client/.env` 中以 `VITE_` 前缀命名。\n\n### 3. 启动开发环境\n在两个终端分别：\n```bash\n# 后端\ncd server\nnpm start\n\n# 前端\ncd client\nnpm run dev\n```\n前端默认在 http://localhost:5173 ，后端在 http://localhost:3000\n\n## 运行流程概览\n1. 注册用户：POST /register\n2. 登录：POST /login -\u003e 得到 JWT\n3. （可选）启用 2FA：\n   - GET /2fa/setup -\u003e 返回 otpauth_url + QR 二维码 DataURL\n   - POST /2fa/verify 发送首次验证码确认\n4. 管理卡片：\n   - 列表：GET /cards (无需 2FA)\n   - 查看详情：GET /cards/:id (需 2FA)\n   - 新增：POST /cards (无需 2FA)\n   - 修改：PUT /cards/:id (需 2FA)\n   - 删除：DELETE /cards/:id (需 2FA)\n5. 备份：POST /backup (需 2FA)\n\n所有受保护接口需在 Header 加 `Authorization: Bearer \u003ctoken\u003e`。\n需要 2FA 的接口再提供：\n- `x-totp: \u003c6位码\u003e` 或在 JSON body / query 中 `totpCode`。\n\n## 安全注意事项\n- 不要把生产数据库 (`server/data/database.sqlite`) 或备份文件提交到仓库，已经通过 `.gitignore` 屏蔽。\n- 一定使用强随机的 `JWT_SECRET` 与 `ENCRYPTION_KEY`，并存放在环境变量或秘密管理服务中。\n- 当前代码用于示例，未实现：密码复杂度策略、速率限制、账户锁定、审计日志、多租户隔离、密钥轮换等。生产需补充。\n- WebDAV 备份为明文 SQLite；如需更强安全性，可：\n  1. 再次对整个文件进行对称加密后上传\n  2. 或仅导出加密后的数据片段\n- 浏览器端切勿缓存完整卡号；此示例中只有在详情查看时返回完整信息。\n\n## 卡号识别及特殊规则\n- CHINA T-UNION: 31 开头 19 位，固定显示银行 CHINA T-UNION，有效期强制 12/99\n- eCNY: 0 开头 16 位，CVV 固定 000，有效期 12/99\n- 其它受支持组织：使用 Luhn + 长度校验\n- 未识别模式：允许 1-80 位纯数字（更自由的自定义/虚拟卡）\n\n## 二因素认证重置流程\n重置 TOTP 时，客户端只需验证账号密码：\n1. 调用 `POST /2fa/reset/init { password }`（需登录并在 Authorization header 中携带 JWT），服务端验证账号密码后生成新的 TOTP 秘钥并立即绑定（`totp_secret`），返回 `{ otpauth_url, qrCode }` 供用户导入到身份验证器。\n2. 用户使用新身份验证器生成的验证码登录即可（无需旧验证码）。\n\n## 备份接口使用示例\n```bash\ncurl -X POST http://localhost:3000/backup \\\n  -H \"Authorization: Bearer \u003ctoken\u003e\" \\\n  -H \"x-totp: \u003ccode\u003e\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"url\":\"https://your-webdav.example/remote.php/dav/files/user/\",\"username\":\"user\",\"password\":\"pass\",\"subdir\":\"card_backups\"}'\n```\n返回示例：\n```json\n{\"message\":\"Backup completed successfully\",\"path\":\"card_backups/cardmanager_backup_20250101_101530.sqlite\"}\n```\n\n## 构建生产前端\n```bash\ncd client\nnpm run build\n# 生成文件位于 client/dist/\n```\n将 `client/dist` 部署到任意静态资源服务器（Nginx、CDN、静态托管等）。\n\n## 自动化批量测试 (server_test.py)\n脚本 `server_test.py` 可在根目录批量生成多组织测试卡并调用 `/cards` 创建接口，验证服务端是否正常。\n\n安装依赖（可选，若无 requests 将回退到 urllib）：\n```bash\npip install requests\n```\n\n使用示例：\n```bash\n# 导出登录后获得的 JWT\nexport TOKEN=\"\u003c你的JWT\u003e\"\n# 全量测试所有支持的卡组织\npython server_test.py --base-url http://localhost:3000\n# 仅测试 visa / mastercard / unionpay\npython server_test.py --only visa,mastercard,unionpay\n# 跳过 amex 与 maestro，并输出详细响应\npython server_test.py --skip amex,maestro -v\n# 列出支持的网络\npython server_test.py --list\n# 仅查看将发送的数据（不真正请求）\npython server_test.py --dry-run --only mir,jcb\n# 多轮创建测试\npython server_test.py --only visa,mastercard --rounds 5 -v\n# 同时测试 FPS 账户（每轮为所有银行各创建一个账户）\npython server_test.py --fps -v\n# 指定自定义 FPS 银行子集\npython server_test.py --fps --fps-banks \"HSBC,BOC,ICBC\"\n```\n支持的网络：visa, mastercard, unionpay, mir, amex, ecny, tunion, jcb, discover, diners, maestro\n\n新增 FPS 参数：\n- --fps 启用 FPS 账户测试（创建数量 = 轮数 * 银行数）\n- --fps-banks 指定逗号分隔银行（缺省则调用 /fps/banks，失败时用内置列表）\n\n注意：\n- eCNY / T-UNION 有效期与 CVV 会被服务端调整为 12/99 与 000（eCNY 固定 000，T-UNION 不展示有效期）。\n- Purge 功能（清空信息）会同时删除卡片与 FPS 账户。\n- 其它卡组织使用 Luhn 合法随机号；不要用于真实支付测试。\n- 创建后脚本会再次调用 GET /cards 输出最新条目。\n\n参数说明：\n- --token/-t 或环境变量 TOKEN 指定 JWT\n- --base-url/-u 指定服务端地址（默认 http://localhost:3000）\n- --only 仅测试逗号分隔列出的网络\n- --skip 跳过指定网络\n- --dry-run 只打印将发送的 payload\n- --verbose/-v 打印详细响应\n- --list 仅列出支持网络\n- --rounds N  重复执行 N 轮（每轮每种网络 1 张）\n\nUI \u0026 测试更新：\n- 清空所有卡片使用自定义模态（非浏览器 prompt），输入主密码与 2FA 验证码。\n- Mastercard 2-series 检测与生成修复为官方区间 222100–272099。\n\n## 常见问题 (FAQ)\nQ: 数据库文件在哪？\nA: `server/data/database.sqlite`（首次运行后生成）。\n\nQ: 如何重置管理员密码？\nA: 当前没有区分角色。可停止服务，删除数据库文件重新注册，或直接用 sqlite3 CLI 修改 `users` 表。\n\nQ: 支持多用户隔离吗？\nA: 是。`cards` 表 `user_id` 外键绑定，查询均按用户过滤。\n\n## 许可证\n[GPL-3.0](LICENSE)\n\n## 共创与 AI 协作\n详见 [AI_CO_CREATION.md](AI_CO_CREATION.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flzhbhlrpython%2Fcard-wallet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flzhbhlrpython%2Fcard-wallet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flzhbhlrpython%2Fcard-wallet/lists"}