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, } }