Improve login UX for unconfirmed email + resend confirmation

- LoginForm: detect 403 separately, show amber warning with email address
- Add 'Resend confirmation email' button (calls POST /api/auth/resend-confirmation)
- Backend: POST /api/auth/resend-confirmation endpoint (generates new token, resends email)
- api.ts: add resendConfirmation method

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-17 19:05:55 +03:00
parent 352d22a101
commit e3f37744b0
3 changed files with 93 additions and 11 deletions

View File

@@ -225,6 +225,44 @@ const auth: FastifyPluginAsync = async (fastify) => {
},
)
// ── POST /api/auth/resend-confirmation ────────────────────────────────────
fastify.post<{ Body: { email: string } }>(
'/api/auth/resend-confirmation',
{
schema: {
body: {
type: 'object',
required: ['email'],
properties: { email: { type: 'string' } },
},
},
},
async (request, reply) => {
const { email } = request.body
const { rows } = await db.query(
`SELECT id, name FROM users WHERE email = $1 AND email_confirmed = false`,
[email.toLowerCase().trim()],
)
// Always return 200 to prevent enumeration
if (rows.length === 0) return reply.code(200).send({ ok: true })
const user = rows[0]
const confirmToken = crypto.randomBytes(32).toString('hex')
await db.query(
`UPDATE users SET confirmation_token = $1, confirmation_sent_at = NOW() WHERE id = $2`,
[confirmToken, user.id],
)
try {
await sendConfirmationEmail(email, user.name as string, confirmToken)
} catch (err) {
console.error('[email] Resend confirmation failed:', err)
}
return reply.code(200).send({ ok: true })
},
)
// ── POST /api/auth/refresh ─────────────────────────────────────────────────
fastify.post('/api/auth/refresh', async (request, reply) => {
const refreshToken = request.cookies?.refresh_token