Commit 9a6089c7c00222faff84fda42585cd80850a605f
1 parent
57d9cf5d
fix(server): 骨架验收等待真实验证码投递;通过通知允许本地 http 链接
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Showing
3 changed files
with
10 additions
and
2 deletions
apps/voko-server/tools/scenarios/voko-alpha/skeleton.mjs
| @@ -60,7 +60,12 @@ try { | @@ -60,7 +60,12 @@ try { | ||
| 60 | 60 | ||
| 61 | const sent = await call('POST', '/api/voko/auth/send-code', { body: { email, locale: 'en' } }) | 61 | const sent = await call('POST', '/api/voko/auth/send-code', { body: { email, locale: 'en' } }) |
| 62 | check('send-code', sent.status === 200, sent) | 62 | check('send-code', sent.status === 200, sent) |
| 63 | - const log = await db('email_delivery_logs').where({ to_email: email, template: 'verify-code', status: 'sent' }).orderBy('created_at', 'desc').first() | 63 | + // auth.sendCode 不等待 SMTP 就返回,这里等真实投递落库(最多 30 秒)。 |
| 64 | + let log | ||
| 65 | + for (let i = 0; i < 30 && log === undefined; i += 1) { | ||
| 66 | + log = await db('email_delivery_logs').where({ to_email: email, template: 'verify-code', status: 'sent' }).orderBy('created_at', 'desc').first() | ||
| 67 | + if (log === undefined) await new Promise((resolve) => setTimeout(resolve, 1000)) | ||
| 68 | + } | ||
| 64 | check('验证码邮件已投递并记录', log !== undefined, { email }) | 69 | check('验证码邮件已投递并记录', log !== undefined, { email }) |
| 65 | const code = JSON.parse(log.metadata_json).code | 70 | const code = JSON.parse(log.metadata_json).code |
| 66 | 71 |
docs/voko/upstream.md
| @@ -17,6 +17,7 @@ | @@ -17,6 +17,7 @@ | ||
| 17 | | `tools/scripts/mysql-dev.js` | 默认库 `voko`,默认 env 文件 `apps/voko-server/.env` | | 17 | | `tools/scripts/mysql-dev.js` | 默认库 `voko`,默认 env 文件 `apps/voko-server/.env` | |
| 18 | | `packages/llm-router/package.json` | 测试脚本加 `--passWithNoTests`(上游该包目前没有测试文件,`vitest run` 会以 1 退出) | | 18 | | `packages/llm-router/package.json` | 测试脚本加 `--passWithNoTests`(上游该包目前没有测试文件,`vitest run` 会以 1 退出) | |
| 19 | | `pnpm-workspace.yaml` | 只保留 `apps/*`、`packages/*` | | 19 | | `pnpm-workspace.yaml` | 只保留 `apps/*`、`packages/*` | |
| 20 | +| `packages/email/src/templates/waitlist-invite.js` | `inviteUrl` 允许 `http://localhost`(本地验收用),其余仍须 https(2026-10-10) | | ||
| 20 | | `apps/voko-server/src/assembly/security.js`、`google-oauth.js`、`email-delivery-log.js` | 从 visual-game-server 原样复制(security.js 只改了一条报错文案) | | 21 | | `apps/voko-server/src/assembly/security.js`、`google-oauth.js`、`email-delivery-log.js` | 从 visual-game-server 原样复制(security.js 只改了一条报错文案) | |
| 21 | 22 | ||
| 22 | ## 待裁剪(原样搬入,后续处理) | 23 | ## 待裁剪(原样搬入,后续处理) |
packages/email/src/templates/waitlist-invite.js
| @@ -69,7 +69,9 @@ function requiredHttpsUrl(value) { | @@ -69,7 +69,9 @@ function requiredHttpsUrl(value) { | ||
| 69 | if (typeof value !== 'string' || value.trim() === '') fail('missing field "inviteUrl"') | 69 | if (typeof value !== 'string' || value.trim() === '') fail('missing field "inviteUrl"') |
| 70 | const url = value.trim() | 70 | const url = value.trim() |
| 71 | try { | 71 | try { |
| 72 | - if (new URL(url).protocol === 'https:') return url | 72 | + const parsed = new URL(url) |
| 73 | + // 本地开发的前端是 http://localhost,其余环境必须 https。 | ||
| 74 | + if (parsed.protocol === 'https:' || parsed.hostname === 'localhost') return url | ||
| 73 | } catch (error) { | 75 | } catch (error) { |
| 74 | fail(`inviteUrl is invalid: ${error instanceof Error ? error.message : String(error)}`) | 76 | fail(`inviteUrl is invalid: ${error instanceof Error ? error.message : String(error)}`) |
| 75 | } | 77 | } |