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 | 60 | |
| 61 | 61 | const sent = await call('POST', '/api/voko/auth/send-code', { body: { email, locale: 'en' } }) |
| 62 | 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 | 69 | check('验证码邮件已投递并记录', log !== undefined, { email }) |
| 65 | 70 | const code = JSON.parse(log.metadata_json).code |
| 66 | 71 | ... | ... |
docs/voko/upstream.md
| ... | ... | @@ -17,6 +17,7 @@ |
| 17 | 17 | | `tools/scripts/mysql-dev.js` | 默认库 `voko`,默认 env 文件 `apps/voko-server/.env` | |
| 18 | 18 | | `packages/llm-router/package.json` | 测试脚本加 `--passWithNoTests`(上游该包目前没有测试文件,`vitest run` 会以 1 退出) | |
| 19 | 19 | | `pnpm-workspace.yaml` | 只保留 `apps/*`、`packages/*` | |
| 20 | +| `packages/email/src/templates/waitlist-invite.js` | `inviteUrl` 允许 `http://localhost`(本地验收用),其余仍须 https(2026-10-10) | | |
| 20 | 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 | 69 | if (typeof value !== 'string' || value.trim() === '') fail('missing field "inviteUrl"') |
| 70 | 70 | const url = value.trim() |
| 71 | 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 | 75 | } catch (error) { |
| 74 | 76 | fail(`inviteUrl is invalid: ${error instanceof Error ? error.message : String(error)}`) |
| 75 | 77 | } | ... | ... |