import { Db } from "mongodb"; import uuid from "uuid"; import { Omit, Sub } from "coral-common/types"; import { GQLUSER_ROLE } from "coral-server/graph/schema/__generated__/types"; import { TenantResource } from "coral-server/models/tenant"; import { invites as collection } from "coral-server/services/mongodb/collections"; export interface Invite extends TenantResource { readonly id: string; email: string; role: GQLUSER_ROLE; expiresAt: Date; createdBy: string; createdAt: Date; } export type CreateInviteInput = Omit< Invite, "id" | "createdAt" | "tenantID" | "createdBy" >; export async function createInvite( mongo: Db, tenantID: string, { email, ...input }: CreateInviteInput, createdBy: string, now = new Date() ) { // Create an ID for the Invite. const id = uuid.v4(); // defaults are the properties set by the application when a new Invite is // created. const defaults: Sub = { id, tenantID, createdBy, createdAt: now, }; // Merge the defaults and the input together. const invite: Readonly = { ...defaults, ...input, email: email.toLowerCase(), }; // Insert it into the database. This may throw an error. await collection(mongo).insertOne(invite); return invite; } export async function redeemInvite(mongo: Db, tenantID: string, id: string) { // Try to snag the invite from the database safely. const result = await collection(mongo).findOneAndDelete({ id, tenantID }, {}); if (!result.value) { throw new Error("an unexpected error occurred"); } return result.value; } export async function redeemInviteFromEmail( mongo: Db, tenantID: string, email: string ) { // Try to snag the invite from the database safely. const result = await collection(mongo).findOneAndDelete( { email, tenantID }, {} ); return result.value || null; } export async function retrieveInviteFromEmail( mongo: Db, tenantID: string, email: string ) { return collection(mongo).findOne({ tenantID, email: email.toLowerCase(), }); } export async function retrieveInvite(mongo: Db, tenantID: string, id: string) { return collection(mongo).findOne({ tenantID, id, }); }