brf/server/routes/api/invites.ts
2025-12-18 07:31:37 +01:00

112 lines
2.8 KiB
TypeScript

import { Type, type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'
import config from '../../config.ts'
import knex from '../../lib/knex.ts'
import emitter from '../../lib/emitter.ts'
import sendMail from '../../lib/send_mail.ts'
import Queries from '../../services/invites/queries.ts'
import inviteEmailTemplate from '../../templates/emails/invite.ts'
import { RoleSchema } from './roles.ts'
export const InviteSchema = Type.Object({
id: Type.Number(),
email: Type.String({ format: 'email' }),
token: Type.String(),
roles: Type.Array(RoleSchema),
createdAt: Type.String(),
createdById: Type.Number(),
createdBy: Type.Object({}),
consumedAt: [Type.String(), Type.Null()],
consumedById: [Type.Number(), Type.Null()],
consumedBy: [Type.Object({}), Type.Null()],
})
const invitesPlugin: FastifyPluginCallbackTypebox = (fastify, _option, done) => {
const queries = Queries({ emitter, knex })
fastify.addHook('onRequest', fastify.auth)
fastify.route({
url: '/',
method: 'GET',
schema: {
querystring: InviteSchema,
response: {
200: InviteSchema,
},
},
handler(request) {
return queries.find(request.query)
},
})
fastify.route({
url: '/',
method: 'POST',
schema: {
body: {
type: Type.Object({
email: Type.String({ format: 'email' }),
roles: Type.Array(Type.Number()),
}),
properties: {
email: { type: 'string' },
roles: { type: 'array', items: { type: 'integer' } },
},
required: ['email', 'roles'],
},
response: { 201: InviteSchema },
},
async handler(request, reply) {
const trx = await knex.transaction()
const invite = await queries.create(
{
...request.body,
createdById: request.session.userId,
},
trx,
)
try {
const link = `${new URL(config.auth.paths.register, config.site.url)}?email=${invite.email}&token=${invite.token}`
await sendMail({
to: invite.email,
subject: `Invite to ${config.site.title} Admin`,
html: await inviteEmailTemplate({ link }).text(),
})
await trx.commit()
return reply.header('Location', `${request.url}/${invite.id}`).status(201).send(invite)
} catch (err) {
await trx.rollback()
throw err
}
},
})
fastify.route({
url: '/:id',
method: 'DELETE',
schema: {
params: Type.Object({
id: Type.Number(),
}),
response: {
204: {},
404: {},
},
},
handler(request, reply) {
return queries.removeById(request.params.id).then((count) => reply.status(count > 0 ? 204 : 404).send())
},
})
done()
}
export default invitesPlugin