From f25e794e7c0ef3ab716c4fa77ac747bce7405899 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Mon, 20 Oct 2025 16:01:40 -0700 Subject: [PATCH] add checks to prevent fk failure in ensureActions --- server/setup/ensureActions.ts | 68 +++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/server/setup/ensureActions.ts b/server/setup/ensureActions.ts index 7fd5384a..587da479 100644 --- a/server/setup/ensureActions.ts +++ b/server/setup/ensureActions.ts @@ -1,41 +1,57 @@ import { ActionsEnum } from "@server/auth/actions"; -import { db } from "@server/db"; +import { db, orgs } from "@server/db"; import { actions, roles, roleActions } from "@server/db"; import { eq, inArray } from "drizzle-orm"; import logger from "@server/logger"; export async function ensureActions() { - const actionIds = Object.values(ActionsEnum); - const existingActions = await db.select().from(actions).execute(); - const existingActionIds = existingActions.map((action) => action.actionId); - - const actionsToAdd = actionIds.filter( - (id) => !existingActionIds.includes(id) - ); - const actionsToRemove = existingActionIds.filter( - (id) => !actionIds.includes(id as ActionsEnum) - ); - - const defaultRoles = await db - .select() - .from(roles) - .where(eq(roles.isAdmin, true)) - .execute(); - await db.transaction(async (trx) => { + const actionIds = Object.values(ActionsEnum); + const existingActions = await trx.select().from(actions).execute(); + const existingActionIds = existingActions.map( + (action) => action.actionId + ); + + const actionsToAdd = actionIds.filter( + (id) => !existingActionIds.includes(id) + ); + const actionsToRemove = existingActionIds.filter( + (id) => !actionIds.includes(id as ActionsEnum) + ); + + const defaultRoles = await trx + .select() + .from(roles) + .where(eq(roles.isAdmin, true)) + .execute(); + + const allOrgs = await trx + .select({ orgId: orgs.orgId }) + .from(orgs) + .execute(); + const allOrgIds = new Set(allOrgs.map((o) => o.orgId)); + const validRoles = defaultRoles.filter( + (r) => r.orgId && r.roleId && allOrgIds.has(r.orgId) + ); + + const skipped = defaultRoles.length - validRoles.length; + if (skipped > 0) { + logger.warn(`Skipped ${skipped} orphaned admin roles missing orgs`); + } + // Add new actions for (const actionId of actionsToAdd) { logger.debug(`Adding action: ${actionId}`); await trx.insert(actions).values({ actionId }).execute(); // Add new actions to the Default role - if (defaultRoles.length != 0) { + if (validRoles.length != 0) { await trx .insert(roleActions) .values( - defaultRoles.map((role) => ({ - roleId: role.roleId!, + validRoles.map((role) => ({ + roleId: role.roleId, actionId, - orgId: role.orgId! + orgId: role.orgId })) ) .execute(); @@ -45,14 +61,14 @@ export async function ensureActions() { // Remove deprecated actions if (actionsToRemove.length > 0) { logger.debug(`Removing actions: ${actionsToRemove.join(", ")}`); - await trx - .delete(actions) - .where(inArray(actions.actionId, actionsToRemove)) - .execute(); await trx .delete(roleActions) .where(inArray(roleActions.actionId, actionsToRemove)) .execute(); + await trx + .delete(actions) + .where(inArray(actions.actionId, actionsToRemove)) + .execute(); } }); }