import type { RouteHandler } from 'fastify' import * as z from 'zod' import config from '../../../config.ts' import sendMail from '../../../lib/send_mail.ts' import StatusError from '../../../lib/status_error.ts' import { StatusErrorSchema } from '../../../schemas/status_error.ts' import { generateToken } from '../helpers.ts' import forgotPasswordTemplate from '../../../templates/emails/forgot_password.ts' const { errors } = config.auth const BodySchema = z.object({ email: z.email(), }) const ResponseSchema = { 204: {}, '4XX': StatusErrorSchema, } const forgotPassword: RouteHandler<{ Body: z.infer }> = async function (request, reply) { const { db } = request.server const { email } = request.body try { const user = await db.selectFrom('user').select('id').where('email', '=', email).executeTakeFirst() if (!user) throw new StatusError(...errors.noUserFound) const token = generateToken() await db.insertInto('passwordToken').values({ userId: user.id, token }).execute() const link = `${new URL('/admin/change-password', config.site.url)}?email=${encodeURI(email)}&token=${token}` await sendMail({ to: email, subject: `Reset password to ${config.site.title}!`, html: await forgotPasswordTemplate({ link, site: config.site }).text(), }) return reply.status(204).send() } catch (err) { this.log.error(err) if (err instanceof StatusError) return reply.status(err.status).send(err.toJSON()) else throw err } } export default { handler: forgotPassword, schema: { body: BodySchema, response: ResponseSchema, }, }