From 4eb49e3e603027495c28b7d4af1fbc71b7f44372 Mon Sep 17 00:00:00 2001 From: Owen Date: Thu, 7 May 2026 15:40:34 -0700 Subject: [PATCH] Make the rebuild long running function background --- server/lib/calculateUserClientsForOrgs.ts | 6 +-- server/private/routers/user/addUserRole.ts | 24 ++++++--- server/private/routers/user/removeUserRole.ts | 17 +++++-- .../private/routers/user/setUserOrgRoles.ts | 17 +++++-- server/routers/auth/deleteMyAccount.ts | 9 +++- server/routers/client/createClient.ts | 24 +++++++-- server/routers/client/createUserClient.ts | 14 ++++-- server/routers/client/deleteClient.ts | 36 +++++++++---- server/routers/olm/createUserOlm.ts | 23 +++++---- server/routers/olm/deleteUserOlm.ts | 39 ++++++++------- .../batchAddClientToSiteResources.ts | 9 +++- server/routers/user/acceptInvite.ts | 24 ++++++--- server/routers/user/addUserRoleLegacy.ts | 18 +++++-- server/routers/user/adminRemoveUser.ts | 8 ++- server/routers/user/createOrgUser.ts | 50 ++++++++++++------- server/routers/user/removeUserOrg.ts | 26 +++------- 16 files changed, 230 insertions(+), 114 deletions(-) diff --git a/server/lib/calculateUserClientsForOrgs.ts b/server/lib/calculateUserClientsForOrgs.ts index 7d8f41a1e..6354dd81f 100644 --- a/server/lib/calculateUserClientsForOrgs.ts +++ b/server/lib/calculateUserClientsForOrgs.ts @@ -25,9 +25,9 @@ import { tierMatrix } from "./billing/tierMatrix"; export async function calculateUserClientsForOrgs( userId: string, - trx?: Transaction + trx: Transaction | typeof db = db ): Promise { - const execute = async (transaction: Transaction) => { + const execute = async (transaction: Transaction | typeof db) => { const orgCache = new Map(); const adminRoleCache = new Map< string, @@ -437,7 +437,7 @@ export async function calculateUserClientsForOrgs( async function cleanupOrphanedClients( userId: string, - trx: Transaction, + trx: Transaction | typeof db, userOrgIds: string[] = [] ): Promise { // Find all OLM clients for this user that should be deleted diff --git a/server/private/routers/user/addUserRole.ts b/server/private/routers/user/addUserRole.ts index 0789373a0..90fa79ee3 100644 --- a/server/private/routers/user/addUserRole.ts +++ b/server/private/routers/user/addUserRole.ts @@ -14,7 +14,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import stoi from "@server/lib/stoi"; -import { clients, db } from "@server/db"; +import { clients, db, primaryDb, Client } from "@server/db"; import { userOrgRoles, userOrgs, roles } from "@server/db"; import { eq, and } from "drizzle-orm"; import response from "@server/lib/response"; @@ -122,8 +122,12 @@ export async function addUserRole( ); } - let newUserRole: { userId: string; orgId: string; roleId: number } | null = - null; + let newUserRole: { + userId: string; + orgId: string; + roleId: number; + } | null = null; + let orgClientsToRebuild: Client[] = []; await db.transaction(async (trx) => { const inserted = await trx .insert(userOrgRoles) @@ -149,11 +153,19 @@ export async function addUserRole( ) ); - for (const orgClient of orgClients) { - await rebuildClientAssociationsFromClient(orgClient, trx); - } + orgClientsToRebuild = orgClients; }); + for (const orgClient of orgClientsToRebuild) { + rebuildClientAssociationsFromClient(orgClient, primaryDb).catch( + (e) => { + logger.error( + `Failed to rebuild client associations for client ${orgClient.clientId} after adding role: ${e}` + ); + } + ); + } + return response(res, { data: newUserRole ?? { userId, orgId: role.orgId, roleId }, success: true, diff --git a/server/private/routers/user/removeUserRole.ts b/server/private/routers/user/removeUserRole.ts index bd5c530d2..1a7b763d4 100644 --- a/server/private/routers/user/removeUserRole.ts +++ b/server/private/routers/user/removeUserRole.ts @@ -14,7 +14,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import stoi from "@server/lib/stoi"; -import { db } from "@server/db"; +import { db, primaryDb, Client } from "@server/db"; import { userOrgRoles, userOrgs, roles, clients } from "@server/db"; import { eq, and } from "drizzle-orm"; import response from "@server/lib/response"; @@ -129,6 +129,7 @@ export async function removeUserRole( } } + let orgClientsToRebuild: Client[] = []; await db.transaction(async (trx) => { await trx .delete(userOrgRoles) @@ -150,11 +151,19 @@ export async function removeUserRole( ) ); - for (const orgClient of orgClients) { - await rebuildClientAssociationsFromClient(orgClient, trx); - } + orgClientsToRebuild = orgClients; }); + for (const orgClient of orgClientsToRebuild) { + rebuildClientAssociationsFromClient(orgClient, primaryDb).catch( + (e) => { + logger.error( + `Failed to rebuild client associations for client ${orgClient.clientId} after removing role: ${e}` + ); + } + ); + } + return response(res, { data: { userId, orgId: role.orgId, roleId }, success: true, diff --git a/server/private/routers/user/setUserOrgRoles.ts b/server/private/routers/user/setUserOrgRoles.ts index d1df4965a..7567ffc54 100644 --- a/server/private/routers/user/setUserOrgRoles.ts +++ b/server/private/routers/user/setUserOrgRoles.ts @@ -13,7 +13,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { clients, db } from "@server/db"; +import { clients, db, primaryDb, Client } from "@server/db"; import { userOrgRoles, userOrgs, roles } from "@server/db"; import { eq, and, inArray } from "drizzle-orm"; import response from "@server/lib/response"; @@ -115,6 +115,7 @@ export async function setUserOrgRoles( ); } + let orgClientsToRebuild: Client[] = []; await db.transaction(async (trx) => { await trx .delete(userOrgRoles) @@ -142,11 +143,19 @@ export async function setUserOrgRoles( and(eq(clients.userId, userId), eq(clients.orgId, orgId)) ); - for (const orgClient of orgClients) { - await rebuildClientAssociationsFromClient(orgClient, trx); - } + orgClientsToRebuild = orgClients; }); + for (const orgClient of orgClientsToRebuild) { + rebuildClientAssociationsFromClient(orgClient, primaryDb).catch( + (e) => { + logger.error( + `Failed to rebuild client associations for client ${orgClient.clientId} after setting roles: ${e}` + ); + } + ); + } + return response(res, { data: { userId, orgId, roleIds: uniqueRoleIds }, success: true, diff --git a/server/routers/auth/deleteMyAccount.ts b/server/routers/auth/deleteMyAccount.ts index 07bdf883d..d03af5631 100644 --- a/server/routers/auth/deleteMyAccount.ts +++ b/server/routers/auth/deleteMyAccount.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db, orgs, userOrgs, users } from "@server/db"; +import { db, orgs, userOrgs, users, primaryDb } from "@server/db"; import { eq, and, inArray, not } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; @@ -218,13 +218,18 @@ export async function deleteMyAccount( await db.transaction(async (trx) => { await trx.delete(users).where(eq(users.userId, userId)); - await calculateUserClientsForOrgs(userId, trx); // loop through the other orgs and decrement the count for (const userOrg of otherOrgsTheUserWasIn) { await usageService.add(userOrg.orgId, FeatureId.USERS, -1, trx); } }); + calculateUserClientsForOrgs(userId, primaryDb).catch((e) => { + logger.error( + `Failed to calculate user clients after deleting account for user ${userId}: ${e}` + ); + }); + try { await invalidateSession(session.sessionId); } catch (error) { diff --git a/server/routers/client/createClient.ts b/server/routers/client/createClient.ts index 337d7e714..029f3c159 100644 --- a/server/routers/client/createClient.ts +++ b/server/routers/client/createClient.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db } from "@server/db"; +import { db, primaryDb } from "@server/db"; import { roles, Client, @@ -92,7 +92,10 @@ export async function createClient( const { orgId } = parsedParams.data; - if (req.user && (!req.userOrgRoleIds || req.userOrgRoleIds.length === 0)) { + if ( + req.user && + (!req.userOrgRoleIds || req.userOrgRoleIds.length === 0) + ) { return next( createHttpError(HttpCode.FORBIDDEN, "User does not have a role") ); @@ -198,7 +201,10 @@ export async function createClient( if (!randomExitNode) { return next( - createHttpError(HttpCode.NOT_FOUND, `No exit nodes available. ${build == "saas" ? "Please contact support." : "You need to install gerbil to use the clients."}`) + createHttpError( + HttpCode.NOT_FOUND, + `No exit nodes available. ${build == "saas" ? "Please contact support." : "You need to install gerbil to use the clients."}` + ) ); } @@ -256,10 +262,18 @@ export async function createClient( clientId: newClient.clientId, dateCreated: moment().toISOString() }); - - await rebuildClientAssociationsFromClient(newClient, trx); }); + if (newClient) { + rebuildClientAssociationsFromClient(newClient, primaryDb).catch( + (e) => { + logger.error( + `Failed to rebuild client associations after creating client: ${e}` + ); + } + ); + } + return response(res, { data: newClient, success: true, diff --git a/server/routers/client/createUserClient.ts b/server/routers/client/createUserClient.ts index d61eab15f..e702796fd 100644 --- a/server/routers/client/createUserClient.ts +++ b/server/routers/client/createUserClient.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db } from "@server/db"; +import { db, primaryDb } from "@server/db"; import { roles, Client, @@ -237,10 +237,18 @@ export async function createUserClient( userId, clientId: newClient.clientId }); - - await rebuildClientAssociationsFromClient(newClient, trx); }); + if (newClient) { + rebuildClientAssociationsFromClient(newClient, primaryDb).catch( + (e) => { + logger.error( + `Failed to rebuild client associations after creating user client: ${e}` + ); + } + ); + } + return response(res, { data: newClient, success: true, diff --git a/server/routers/client/deleteClient.ts b/server/routers/client/deleteClient.ts index 276bfde96..378639af6 100644 --- a/server/routers/client/deleteClient.ts +++ b/server/routers/client/deleteClient.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db, olms } from "@server/db"; +import { db, olms, primaryDb, Client, Olm } from "@server/db"; import { clients, clientSitesAssociationsCache } from "@server/db"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; @@ -71,14 +71,17 @@ export async function deleteClient( ); } + let deletedClient: Client | undefined; + let olm: Olm | undefined; + await db.transaction(async (trx) => { // Then delete the client itself - const [deletedClient] = await trx + [deletedClient] = await trx .delete(clients) .where(eq(clients.clientId, clientId)) .returning(); - const [olm] = await trx + [olm] = await trx .select() .from(olms) .where(eq(olms.clientId, clientId)) @@ -88,14 +91,29 @@ export async function deleteClient( if (!client.userId && client.olmId) { await trx.delete(olms).where(eq(olms.olmId, client.olmId)); } - - await rebuildClientAssociationsFromClient(deletedClient, trx); - - if (olm) { - await sendTerminateClient(deletedClient.clientId, OlmErrorCodes.TERMINATED_DELETED, olm.olmId); // the olmId needs to be provided because it cant look it up after deletion - } }); + if (deletedClient) { + rebuildClientAssociationsFromClient(deletedClient, primaryDb).catch( + (e) => { + logger.error( + `Failed to rebuild client associations after deleting client ${clientId}: ${e}` + ); + } + ); + if (olm) { + sendTerminateClient( + deletedClient.clientId, + OlmErrorCodes.TERMINATED_DELETED, + olm.olmId + ).catch((e) => { + logger.error( + `Failed to send terminate message for client ${deletedClient?.clientId} after deleting client ${clientId}: ${e}` + ); + }); + } + } + return response(res, { data: null, success: true, diff --git a/server/routers/olm/createUserOlm.ts b/server/routers/olm/createUserOlm.ts index 0fc1e452d..105a94ff2 100644 --- a/server/routers/olm/createUserOlm.ts +++ b/server/routers/olm/createUserOlm.ts @@ -1,5 +1,5 @@ import { NextFunction, Request, Response } from "express"; -import { db, olms } from "@server/db"; +import { db, olms, primaryDb } from "@server/db"; import HttpCode from "@server/types/HttpCode"; import { z } from "zod"; import createHttpError from "http-errors"; @@ -81,16 +81,19 @@ export async function createUserOlm( const secretHash = await hashPassword(secret); - await db.transaction(async (trx) => { - await trx.insert(olms).values({ - olmId: olmId, - userId, - name, - secretHash, - dateCreated: moment().toISOString() - }); + await db.insert(olms).values({ + olmId: olmId, + userId, + name, + secretHash, + dateCreated: moment().toISOString() + }); - await calculateUserClientsForOrgs(userId, trx); + calculateUserClientsForOrgs(userId, primaryDb).catch((e) => { + console.error( + "Error calculating user clients after creating olm:", + e + ); }); return response(res, { diff --git a/server/routers/olm/deleteUserOlm.ts b/server/routers/olm/deleteUserOlm.ts index 2c2814899..fb1f5604c 100644 --- a/server/routers/olm/deleteUserOlm.ts +++ b/server/routers/olm/deleteUserOlm.ts @@ -1,5 +1,5 @@ import { NextFunction, Request, Response } from "express"; -import { Client, db } from "@server/db"; +import { Client, db, Olm, primaryDb } from "@server/db"; import { olms, clients, clientSitesAssociationsCache } from "@server/db"; import { eq } from "drizzle-orm"; import HttpCode from "@server/types/HttpCode"; @@ -49,6 +49,7 @@ export async function deleteUserOlm( const { olmId } = parsedParams.data; + let deletedClient: Client | undefined; // Delete associated clients and the OLM in a transaction await db.transaction(async (trx) => { // Find all clients associated with this OLM @@ -57,7 +58,6 @@ export async function deleteUserOlm( .from(clients) .where(eq(clients.olmId, olmId)); - let deletedClient: Client | null = null; // Delete all associated clients if (associatedClients.length > 0) { [deletedClient] = await trx @@ -67,23 +67,28 @@ export async function deleteUserOlm( } // Finally, delete the OLM itself - const [olm] = await trx - .delete(olms) - .where(eq(olms.olmId, olmId)) - .returning(); - - if (deletedClient) { - await rebuildClientAssociationsFromClient(deletedClient, trx); - if (olm) { - await sendTerminateClient( - deletedClient.clientId, - OlmErrorCodes.TERMINATED_DELETED, - olm.olmId - ); // the olmId needs to be provided because it cant look it up after deletion - } - } + await trx.delete(olms).where(eq(olms.olmId, olmId)).returning(); }); + if (deletedClient) { + rebuildClientAssociationsFromClient(deletedClient, primaryDb).catch( + (e) => { + logger.error( + `Failed to rebuild client-site associations after deleting OLM ${olmId}: ${e}` + ); + } + ); + sendTerminateClient( + deletedClient.clientId, + OlmErrorCodes.TERMINATED_DELETED, + olmId + ).catch((e) => { + logger.error( + `Failed to send terminate message for client ${deletedClient?.clientId} after deleting OLM ${olmId}: ${e}` + ); + }); + } + return response(res, { data: null, success: true, diff --git a/server/routers/siteResource/batchAddClientToSiteResources.ts b/server/routers/siteResource/batchAddClientToSiteResources.ts index c3ad3859a..34c7b58fe 100644 --- a/server/routers/siteResource/batchAddClientToSiteResources.ts +++ b/server/routers/siteResource/batchAddClientToSiteResources.ts @@ -5,7 +5,8 @@ import { clients, clientSiteResources, siteResources, - apiKeyOrg + apiKeyOrg, + primaryDb } from "@server/db"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; @@ -220,8 +221,12 @@ export async function batchAddClientToSiteResources( siteResourceId: siteResource.siteResourceId }); } + }); - await rebuildClientAssociationsFromClient(client, trx); + rebuildClientAssociationsFromClient(client, primaryDb).catch((e) => { + logger.error( + `Failed to rebuild client associations after batch adding site resources for client ${clientId}: ${e}` + ); }); return response(res, { diff --git a/server/routers/user/acceptInvite.ts b/server/routers/user/acceptInvite.ts index 88010e580..e3366a0c5 100644 --- a/server/routers/user/acceptInvite.ts +++ b/server/routers/user/acceptInvite.ts @@ -1,7 +1,13 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db, orgs } from "@server/db"; -import { roles, userInviteRoles, userInvites, userOrgs, users } from "@server/db"; +import { db, orgs, primaryDb } from "@server/db"; +import { + roles, + userInviteRoles, + userInvites, + userOrgs, + users +} from "@server/db"; import { eq, and, inArray } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; @@ -146,9 +152,7 @@ export async function acceptInvite( .from(userInviteRoles) .where(eq(userInviteRoles.inviteId, inviteId)); - const inviteRoleIds = [ - ...new Set(inviteRoleRows.map((r) => r.roleId)) - ]; + const inviteRoleIds = [...new Set(inviteRoleRows.map((r) => r.roleId))]; if (inviteRoleIds.length === 0) { return next( createHttpError( @@ -193,13 +197,19 @@ export async function acceptInvite( .delete(userInvites) .where(eq(userInvites.inviteId, inviteId)); - await calculateUserClientsForOrgs(existingUser[0].userId, trx); - logger.debug( `User ${existingUser[0].userId} accepted invite to org ${existingInvite.orgId}` ); }); + calculateUserClientsForOrgs(existingUser[0].userId, primaryDb).catch( + (e) => { + logger.error( + `Failed to calculate user clients after accepting invite for user ${existingUser[0].userId}: ${e}` + ); + } + ); + return response(res, { data: { accepted: true, orgId: existingInvite.orgId }, success: true, diff --git a/server/routers/user/addUserRoleLegacy.ts b/server/routers/user/addUserRoleLegacy.ts index db0c6182f..9696e4aac 100644 --- a/server/routers/user/addUserRoleLegacy.ts +++ b/server/routers/user/addUserRoleLegacy.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import stoi from "@server/lib/stoi"; -import { clients, db } from "@server/db"; +import { clients, db, primaryDb, Client } from "@server/db"; import { userOrgRoles, userOrgs, roles } from "@server/db"; import { eq, and } from "drizzle-orm"; import response from "@server/lib/response"; @@ -112,6 +112,8 @@ export async function addUserRoleLegacy( ); } + let orgClientsToRebuild: Client[] = []; + await db.transaction(async (trx) => { await trx .delete(userOrgRoles) @@ -138,11 +140,19 @@ export async function addUserRoleLegacy( ) ); - for (const orgClient of orgClients) { - await rebuildClientAssociationsFromClient(orgClient, trx); - } + orgClientsToRebuild = orgClients; }); + for (const orgClient of orgClientsToRebuild) { + rebuildClientAssociationsFromClient(orgClient, primaryDb).catch( + (e) => { + logger.error( + `Failed to rebuild client associations for client ${orgClient.clientId} after adding role: ${e}` + ); + } + ); + } + return response(res, { data: { ...existingUser, roleId }, success: true, diff --git a/server/routers/user/adminRemoveUser.ts b/server/routers/user/adminRemoveUser.ts index ae7f9f470..38713ce26 100644 --- a/server/routers/user/adminRemoveUser.ts +++ b/server/routers/user/adminRemoveUser.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db } from "@server/db"; +import { db, primaryDb } from "@server/db"; import { users } from "@server/db"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; @@ -53,8 +53,12 @@ export async function adminRemoveUser( await db.transaction(async (trx) => { await trx.delete(users).where(eq(users.userId, userId)); + }); - await calculateUserClientsForOrgs(userId, trx); + calculateUserClientsForOrgs(userId, primaryDb).catch((e) => { + logger.error( + `Failed to calculate user clients after removing user ${userId}: ${e}` + ); }); return response(res, { diff --git a/server/routers/user/createOrgUser.ts b/server/routers/user/createOrgUser.ts index ddc37d3a2..ed3824b24 100644 --- a/server/routers/user/createOrgUser.ts +++ b/server/routers/user/createOrgUser.ts @@ -6,7 +6,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; -import { db, orgs } from "@server/db"; +import { db, orgs, primaryDb } from "@server/db"; import { and, eq, inArray } from "drizzle-orm"; import { idp, idpOidcConfig, roles, userOrgs, users } from "@server/db"; import { generateId } from "@server/auth/sessions/app"; @@ -34,8 +34,7 @@ const bodySchema = z roleId: z.number().int().positive().optional() }) .refine( - (d) => - (d.roleIds != null && d.roleIds.length > 0) || d.roleId != null, + (d) => (d.roleIds != null && d.roleIds.length > 0) || d.roleId != null, { message: "roleIds or roleId is required", path: ["roleIds"] } ) .transform((data) => ({ @@ -100,8 +99,14 @@ export async function createOrgUser( } const { orgId } = parsedParams.data; - const { username, email, name, type, idpId, roleIds: uniqueRoleIds } = - parsedBody.data; + const { + username, + email, + name, + type, + idpId, + roleIds: uniqueRoleIds + } = parsedBody.data; if (build == "saas") { const usage = await usageService.getUsage(orgId, FeatureId.USERS); @@ -232,6 +237,7 @@ export async function createOrgUser( ); } + let userIdForClients: string | undefined; await db.transaction(async (trx) => { const [existingUser] = await trx .select() @@ -270,7 +276,7 @@ export async function createOrgUser( { orgId, userId: existingUser.userId, - autoProvisioned: false, + autoProvisioned: false }, uniqueRoleIds, trx @@ -292,20 +298,30 @@ export async function createOrgUser( }) .returning(); - await assignUserToOrg( - org, - { - orgId, - userId: newUser.userId, - autoProvisioned: false, - }, - uniqueRoleIds, - trx - ); + await assignUserToOrg( + org, + { + orgId, + userId: newUser.userId, + autoProvisioned: false + }, + uniqueRoleIds, + trx + ); } - await calculateUserClientsForOrgs(userId, trx); + userIdForClients = userId; }); + + if (userIdForClients) { + calculateUserClientsForOrgs(userIdForClients, primaryDb).catch( + (e) => { + logger.error( + `Failed to calculate user clients after creating org user: ${e}` + ); + } + ); + } } else { return next( createHttpError(HttpCode.BAD_REQUEST, "User type is required") diff --git a/server/routers/user/removeUserOrg.ts b/server/routers/user/removeUserOrg.ts index 3c86a03c5..fcb3313a8 100644 --- a/server/routers/user/removeUserOrg.ts +++ b/server/routers/user/removeUserOrg.ts @@ -7,7 +7,8 @@ import { siteResources, sites, UserOrg, - userSiteResources + userSiteResources, + primaryDb } from "@server/db"; import { userOrgs, userResources, users, userSites } from "@server/db"; import { and, count, eq, exists, inArray } from "drizzle-orm"; @@ -91,25 +92,12 @@ export async function removeUserOrg( await db.transaction(async (trx) => { await removeUserFromOrg(org, userId, trx); + }); - // if (build === "saas") { - // const [rootUser] = await trx - // .select() - // .from(users) - // .where(eq(users.userId, userId)); - // - // const [leftInOrgs] = await trx - // .select({ count: count() }) - // .from(userOrgs) - // .where(eq(userOrgs.userId, userId)); - // - // // if the user is not an internal user and does not belong to any org, delete the entire user - // if (rootUser?.type !== UserType.Internal && !leftInOrgs.count) { - // await trx.delete(users).where(eq(users.userId, userId)); - // } - // } - - await calculateUserClientsForOrgs(userId, trx); + calculateUserClientsForOrgs(userId, primaryDb).catch((e) => { + logger.error( + `Failed to calculate user clients after removing user ${userId} from org ${orgId}: ${e}` + ); }); return response(res, {