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

93 lines
2.3 KiB
TypeScript

import type EventEmitter from 'node:events'
import crypto from 'node:crypto'
import type { Knex } from 'knex'
import _ from 'lodash'
import type { NewInvite } from './types.ts'
import RestQueriesFactory from '../../lib/knex_rest_queries.ts'
export const columns = [
'id',
'email',
'token',
'createdAt',
'createdById',
'modifiedAt',
'modifiedById',
'consumedAt',
'consumedById',
]
export const Selects = (knex: Knex) => ({
roles: knex
.select(knex.raw('json_agg(roles)'))
.from(
knex
.select('id', 'name')
.from('role')
.innerJoin('invites_roles', 'role.id', 'invites_roles.role_id')
.where('invites_roles.invite_id', knex.ref('invite.id'))
.as('roles'),
),
createdBy: knex
.select(knex.raw('row_to_json(users)'))
.from(knex.select('id', 'email').from('user').where('id', knex.ref('invite.created_by_id')).as('users')),
consumedBy: knex
.select(knex.raw('row_to_json(users)'))
.from(knex.select('id', 'email').from('user').where('id', knex.ref('invite.consumed_by_id')).as('users')),
})
function generateToken(length = 64) {
return crypto.randomBytes(length / 2).toString('hex')
}
export default ({ knex, emitter }: { knex: Knex; emitter: EventEmitter }) => {
const queries = RestQueriesFactory({
emitter,
knex,
table: 'invite',
columns,
selects: Selects(knex),
})
function consume(id: number, consumedById: number, client = knex) {
return client('invite')
.update({ consumed_at: knex.fn.now(), consumed_by_id: consumedById })
.where('id', id)
.then((result: ANY) => {
if (result === 0) throw new Error('No invite was consumed')
return !!result
})
}
async function create(json: NewInvite, client: Knex | Knex.Transaction = knex) {
const token = generateToken()
const trx = client.isTransaction ? (client as Knex.Transaction) : await client.transaction()
const invite = await queries.create({ ..._.omit(json, 'roles'), token }, trx)
const roles = json.roles.map((role: number) => ({
invite_id: invite.id,
role_id: role,
}))
await trx.table('invites_roles').insert(roles)
if (trx !== client) {
await trx.commit()
}
return queries.findById(invite.id, trx)
}
return {
...queries,
consume,
create,
}
}