import type { RouteHandler } from 'fastify' 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' import { Type, type Static } from '@fastify/type-provider-typebox' const { errors, timeouts } = config.auth const QuerystringSchema = Type.Object({ email: Type.String({ format: 'email' }), token: Type.String(), }) const ResponseSchema = { 200: {}, '4XX': StatusErrorSchema, } const verifyEmail: RouteHandler<{ Querystring: Static }> = 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('
Email Verified!
') } catch (err) { this.log.error(err) throw err } } export default { handler: verifyEmail, schema: { querystring: QuerystringSchema, response: ResponseSchema, }, }