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