57 lines
1.6 KiB
TypeScript
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,
|
|
},
|
|
}
|