# Gmail Code **Repository Path**: IT_Ruan/gmail-code ## Basic Information - **Project Name**: Gmail Code - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-06 - **Last Updated**: 2026-07-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Gmail / Microsoft 验证码查询服务 这是一个只读取“已授权个人 Gmail 或 Microsoft 邮箱”的验证码查询服务。它不负责自动注册第三方网站,只提供受控的取码 API 和后台管理页面。 ## 功能 - 多个 Gmail 账号逐个 OAuth 授权接入,Gmail scope 只使用 `gmail.readonly`。 - Microsoft 邮箱通过 Microsoft Graph 或 IMAP XOAUTH2 接入;两种方式都可用时优先 Graph。 - MySQL 8 + Prisma migration。 - 浏览器直开取码:`/api/v1/code?email=...&site=...&waitSeconds=30&token=...`。 - Arco Design 后台管理邮箱、站点规则、Token 和查询日志。 - Gmail refresh token、Microsoft refresh/access token 使用 AES-256-GCM 加密保存。 - 新建 URL Token 会同时保存 hash 和加密明文,后续可在后台复制完整取码链接。 - 查询日志不保存邮件正文,不保存验证码明文。 ## 环境变量 复制模板: ```powershell Copy-Item .env.example .env ``` 生成密钥: ```powershell node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" ``` 生成管理员密码 hash: ```powershell node -e "console.log(require('crypto').createHash('sha256').update('your-admin-password').digest('hex'))" ``` 关键配置: - `TOKEN_ENCRYPTION_KEY`:32 字节 base64,用于加密 Gmail refresh token、Microsoft refresh/access token(包括轮换后的 refresh token)和可复制 Token 明文。 - `SESSION_SECRET`:随机字符串。 - `ACCESS_TOKEN_PEPPER`:随机字符串,用于 Token hash。 - `ADMIN_PASSWORD_HASH`:管理员密码 SHA-256 hex。 - `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET`:Google Cloud OAuth Client。 本地直接跑 Node 时: ```env DATABASE_URL=mysql://gmail_api:change_me@localhost:3306/gmail_api ``` Docker Compose 启动时: ```env DATABASE_URL=mysql://gmail_api:change_me@mysql:3306/gmail_api ``` ## Google OAuth 在 Google Cloud Console 创建 OAuth Client: - Application type:`Web application` - Authorized redirect URI:`http://localhost:3000/oauth/google/callback` - 启用 API:Gmail API - Scope:`https://www.googleapis.com/auth/gmail.readonly` 同一组 `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` 可以授权多个 Gmail 账号。后台生成授权链接后,Google 回调页会展示 OAuth `code` 和完整 callback URL;把完整 callback URL 粘贴回后台提交后,服务端会通过 Gmail profile 自动识别实际授权邮箱,不需要提前填写邮箱地址。 ## Microsoft 邮箱导入 在“邮箱授权”页选择导入 Microsoft 邮箱,每行使用以下格式: ```text email----password----clientId----refreshToken ``` 只使用明显的占位符准备格式,不要把真实密码写入输入框。例如: ```text ------------ ``` `password` 位置仅用于兼容该输入格式。浏览器解析每一行时会立即丢弃该字段,解析结果和请求体只包含 `email`、`clientId`、`refreshToken`,随后清空输入框;服务端导入接口没有密码字段,不接收、入库或记录邮箱密码。密码也不能用来补充 OAuth 权限。 导入使用的 Microsoft OAuth 委托权限至少满足以下一项: - Microsoft Graph:`Mail.Read`;`Mail.ReadWrite` 也可。 - IMAP XOAUTH2:`IMAP.AccessAsUser.All`。 导入时会实际探测可用访问方式;Graph 和 IMAP 都可用时保存为 Graph 模式。两者都不可用或权限不足时,该项导入失败;重新导入失败不会覆盖原有可用凭据。Microsoft 支持不会替换 Gmail,现有 Google OAuth 接入和 Gmail 查询流程继续保留。 ### Microsoft token 生命周期 - Microsoft token 交换固定使用 `https://login.microsoftonline.com/common/oauth2/v2.0/token`。首次使用 refresh token 换取 access token 时不主动附加 `scope`;之后刷新复用 Microsoft 返回并持久化的 scope。 - refresh token 和 access token 都使用现有 `TOKEN_ENCRYPTION_KEY` 做 AES-256-GCM 加密持久化;`clientId` 和 scope 不属于加密字段。 - 查询优先复用仍有超过 5 分钟有效期的 access token 热缓存;命中热缓存时不会请求 Microsoft token endpoint。进入提前失效窗口后自动刷新,同一邮箱的并发刷新会合并。 - 单次取码轮询会复用同一个 access token;IMAP 模式连接 `outlook.office365.com:993` 并在该轮询中复用同一个 XOAUTH2 连接租约。 - 如果 Microsoft 返回轮换后的 refresh token,服务端会加密保存新值。 - refresh grant 返回 `invalid_grant` 时,邮箱会进入 `REAUTH_REQUIRED`。在后台为同一邮箱重新导入一组可用的 `clientId` 和 refresh token,探测成功后即可恢复为 `ACTIVE`。 ### 管理员临时复制 Microsoft access token 管理员可以在邮箱操作中临时获取并复制当前有效的 Microsoft access token。响应带有 `Cache-Control: no-store, private` 和 `Pragma: no-cache`;页面只在当前弹窗状态中展示 token,关闭弹窗后立即清除,不写入浏览器存储。只应在受信任的管理设备上按需执行一次性复制,使用后关闭弹窗并清理剪贴板,不要把 token 粘贴到日志、工单或文档中。 ## 本地启动 安装依赖并初始化 Prisma: ```powershell npm.cmd install npm.cmd run prisma:generate npm.cmd run prisma:deploy ``` 构建后台并启动后端: ```powershell npm.cmd run build:admin npm.cmd run dev ``` 访问后台: ```text http://localhost:3000/admin/login ``` 开发前端时可以开两个终端: ```powershell npm.cmd run dev npm.cmd run dev:admin ``` Vite 开发服务默认地址: ```text http://localhost:5173/admin/mailboxes ``` ## Docker 启动 ```powershell docker compose up --build ``` Docker 会启动 `app` 和 `mysql` 两个服务,MySQL 数据通过 volume 持久化。 ## Google 邮箱接入流程 1. 在“邮箱授权”页点击“生成授权链接”。 2. 复制或打开 Google 授权链接,完成授权。 3. 授权完成后会进入“输入此代码以完成授权”的回调页。 4. 复制完整 callback URL,粘贴到后台“邮箱授权”页右侧输入框并提交。 5. 后台会用 callback URL 中的 `code/state` 换取 Gmail refresh token,并自动识别授权邮箱。 6. 在“站点规则”页创建规则,例如 `site=example`,验证码正则 `\b(\d{6})\b`。 7. 在“访问 Token”页创建 Token,并选择允许访问的邮箱和站点。 8. 如果同一个站点需要给多个 Gmail 生成 Token,可以点击“批量生成”,选择站点和多个 ACTIVE 邮箱。 9. 在 Token 卡片里点击“复制链接”,后台会用唯一绑定的邮箱和站点生成完整取码链接,默认 `waitSeconds=10`。 ## 取码 API(保持兼容) Gmail 和 Microsoft 邮箱共用现有接口,不新增 provider 参数,请求参数以及成功/错误响应结构保持不变: 取码 URL 示例: ```text http://localhost:3000/api/v1/code?email=user@example.com&site=example&waitSeconds=30&token=YOUR_TOKEN ``` `waitSeconds` 可选,不传时只查一次并立即返回;传了就按 URL 参数值等待,不做隐藏截断。 当 Microsoft 邮箱处于 `REAUTH_REQUIRED` 时,接口仍使用现有错误响应结构返回 `MAILBOX_REAUTH_REQUIRED`;管理员完成成功的重新导入后,原 URL 可继续使用。 ## Token 复制 新建 Token 会保存: - `token_hash`:用于真实鉴权。 - `encrypted_token/token_iv/token_tag`:用于后台后续复制完整 URL。 旧 Token 没有加密明文,后台会显示“需重建”。禁用 Token 后仍可看到卡片,但取码 API 会拒绝使用。删除 Token 会硬删除授权记录,历史查询日志仍保留。 “复制链接”按钮会直接复制完整 URL,不再弹出邮箱/站点选择框。为了避免复制到错误邮箱,Token 需要能唯一确定 1 个邮箱和 1 个站点:如果 Token 权限留空但系统里当前只有一个可用邮箱或站点,也可以直接复制;如果范围里有多个邮箱或多个站点,需要重新创建一个更窄权限的 Token。 ## 批量生成站点 Token “访问 Token”页支持按站点批量生成: - 选择 1 个已启用站点。 - 选择多个 `ACTIVE` Gmail 或 Microsoft 邮箱。 - 系统会为每个邮箱各创建 1 个 Token,权限固定为该邮箱和该站点。 - Token 名称格式为 `名称前缀 - email`,名称前缀默认使用站点名。 - 批量结果会展示明文 Token 和完整取码 URL,支持单条复制和复制全部 URL。 - 批量 URL 默认 `waitSeconds=10`,和 Token 卡片“一键复制链接”保持一致。 允许为同一邮箱和站点重复生成多个 Token;旧 Token 可以在卡片里手动删除。 ## Token 里的 Mailboxes / Sites 创建 Token 时: - `Mailboxes` 来自“邮箱授权”页已接入的 Gmail 或 Microsoft 账号。 - `Sites` 来自“站点规则”页已配置的 `site` 标识。 - 两个字段都是下拉多选。 - 留空表示该维度不限制,例如 `Mailboxes` 留空就是允许访问所有已授权邮箱。 建议生产使用时给每个 Token 只选择必要的邮箱和站点。 ## 常用命令 ```powershell npm.cmd test npm.cmd run typecheck npm.cmd run build ``` PowerShell 如果拦截 `npm`,请使用 `npm.cmd`。