import { backfillAccounts, backfillDone } from "./backfill";
import { all } from "./db";
import { accountDb } from "./scoped-db";
import { locatedByPrefix, registerLocator } from "./scoped-db/locators";
const INVITER_INDEX = "org-invitation-inviters";
function inviterKey(inviterId: string, invitationId: string): string {
return `${inviterId}:${invitationId}`;
}
/** Indexes an invitation under who sent it, so clearing that user's invitations needn't visit every org. */
export async function registerInviter(inviterId: string, invitationId: string, orgId: string): Promise<void> {
await registerLocator("org_invitation_by", inviterKey(inviterId, invitationId), orgId);
}
/** Cron step until done: indexes invitations sent before `org_invitation_by` locators existed. */
export function backfillInviters(): Promise<boolean> {
return backfillAccounts(INVITER_INDEX, async (accountId) => {
const rows = await accountDb(accountId).all<{ id: string; org_id: string; invited_by: string }>(
"SELECT id, org_id, invited_by FROM org_invitations WHERE invited_by IS NOT NULL",
);
for (const row of rows) await registerInviter(row.invited_by, row.id, row.org_id);
});
}
/**
* Organizations that may hold invitations to or from `userId`: those in the invitee's own copies, those it sent
* from, and `memberOrgIds`. Every org until the inviter index is backfilled.
*/
export async function invitationOrgs(userId: string, memberOrgIds: string[]): Promise<string[]> {
const [received, sent] = await Promise.all([
accountDb(userId).all<{ org_id: string }>("SELECT DISTINCT org_id FROM org_invitations WHERE user_id = ?", userId),
backfillDone(INVITER_INDEX).then((indexed) =>
indexed
? locatedByPrefix("org_invitation_by", `${userId}:`)
: all<{ id: string }>("SELECT id FROM accounts WHERE kind = 'org'").then((rows) => rows.map((row) => row.id)),
),
]);
return [...new Set([...memberOrgIds, ...received.map((row) => row.org_id), ...sent])].filter((id) => id !== userId);