brf/server/plugins/auth/routes/reset_password.ts
2025-12-18 10:20:02 +01:00

60 lines
1.6 KiB
TypeScript

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<typeof BodySchema> }> = 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,
},
}