brf/server/plugins/auth/routes/verify_email.ts

57 lines
1.6 KiB
TypeScript

import type { RouteHandler } from 'fastify'
import * as z from 'zod'
import config from '../../../config.ts'
import knex from '../../../lib/knex.ts'
import StatusError from '../../../lib/status_error.ts'
import { StatusErrorSchema } from '../../../schemas/status_error.ts'
const { errors, timeouts } = config.auth
const QuerystringSchema = z.object({
email: z.email(),
token: z.string(),
})
const ResponseSchema = {
200: {},
'4XX': StatusErrorSchema,
}
const verifyEmail: RouteHandler<{ Querystring: z.infer<typeof QuerystringSchema> }> = async function (request, reply) {
try {
const { token, email } = request.query
const emailToken = await knex('email_token').first('*').where({ email, token })
if (!emailToken) {
throw new StatusError(...errors.tokenNotFound)
} else if (emailToken.consumed_at) {
throw new StatusError(...errors.tokenConsumed)
} else if (Date.now() > emailToken.created_at.getTime() + timeouts.verifyEmail) {
throw new StatusError(...errors.tokenExpired)
}
await knex.transaction((trx) =>
Promise.all([
trx('user').update({ email_verified_at: knex.fn.now() }).where('id', emailToken.user_id),
trx('email_token').update({ consumed_at: knex.fn.now() }).where('id', emailToken.id),
]),
)
// TODO better template
return reply.status(200).type('text/html').send('<div>Email Verified!</div>')
} catch (err) {
this.log.error(err)
throw err
}
}
export default {
handler: verifyEmail,
schema: {
querystring: QuerystringSchema,
response: ResponseSchema,
},
}