Compare commits

..

2 Commits

Author SHA1 Message Date
Owen Schwartz
784588cebc Merge pull request #3350 from fosrl/dev
Make sure the rebuild actually executes
2026-06-26 09:28:12 -04:00
Owen
2e628fe0e4 Make sure the rebuild actually executes 2026-06-26 09:26:43 -04:00

View File

@@ -29,7 +29,7 @@ type ClientRow = typeof clients.$inferSelect;
function runQueuedClientAssociationRebuilds( function runQueuedClientAssociationRebuilds(
userId: string, userId: string,
queuedClients: ClientRow[] queuedClients: ClientRow[]
): void { ) {
if (queuedClients.length === 0) { if (queuedClients.length === 0) {
return; return;
} }
@@ -39,35 +39,29 @@ function runQueuedClientAssociationRebuilds(
uniqueClientsById.set(client.clientId, client); uniqueClientsById.set(client.clientId, client);
} }
void (async () => {
for (const client of uniqueClientsById.values()) { for (const client of uniqueClientsById.values()) {
try { rebuildClientAssociationsFromClient(client).catch((error) => {
await rebuildClientAssociationsFromClient(client);
} catch (error) {
logger.error( logger.error(
`Failed rebuilding associations for client ${client.clientId} (user ${userId}): ${String(error)}` `Error rebuilding client associations for client ${client.clientId} (user ${userId}): ${String(
error
)}`
); );
} });
} }
logger.debug( logger.debug(
`Queued association rebuild completed for ${uniqueClientsById.size} client(s) (user ${userId})` `Queued association rebuild completed for ${uniqueClientsById.size} client(s) (user ${userId})`
); );
})();
} }
export async function calculateUserClientsForOrgs( export async function calculateUserClientsForOrgs(
userId: string userId: string
): Promise<void> { ): Promise<void> {
const trx = primaryDb; const trx = primaryDb;
const queuedAssociationRebuilds: ClientRow[] = [];
const execute = async (transaction: Transaction | typeof db) => { const queuedAssociationRebuilds: ClientRow[] = [];
const orgCache = new Map<string, typeof orgs.$inferSelect | null>(); const orgCache = new Map<string, typeof orgs.$inferSelect | null>();
const adminRoleCache = new Map< const adminRoleCache = new Map<string, typeof roles.$inferSelect | null>();
string,
typeof roles.$inferSelect | null
>();
const exitNodesCache = new Map< const exitNodesCache = new Map<
string, string,
Awaited<ReturnType<typeof listExitNodes>> Awaited<ReturnType<typeof listExitNodes>>
@@ -80,8 +74,7 @@ export async function calculateUserClientsForOrgs(
const roleClientAccessCache = new Map<string, boolean>(); const roleClientAccessCache = new Map<string, boolean>();
const userClientAccessCache = new Map<string, boolean>(); const userClientAccessCache = new Map<string, boolean>();
const getOrgOlmKey = (orgId: string, olmId: string) => const getOrgOlmKey = (orgId: string, olmId: string) => `${orgId}:${olmId}`;
`${orgId}:${olmId}`;
const getRoleClientKey = (roleId: number, clientId: number) => const getRoleClientKey = (roleId: number, clientId: number) =>
`${roleId}:${clientId}`; `${roleId}:${clientId}`;
const getUserClientKey = (cachedUserId: string, clientId: number) => const getUserClientKey = (cachedUserId: string, clientId: number) =>
@@ -92,7 +85,7 @@ export async function calculateUserClientsForOrgs(
return orgCache.get(orgId) ?? null; return orgCache.get(orgId) ?? null;
} }
const [org] = await transaction const [org] = await trx
.select() .select()
.from(orgs) .from(orgs)
.where(eq(orgs.orgId, orgId)); .where(eq(orgs.orgId, orgId));
@@ -106,7 +99,7 @@ export async function calculateUserClientsForOrgs(
return adminRoleCache.get(orgId) ?? null; return adminRoleCache.get(orgId) ?? null;
} }
const [adminRole] = await transaction const [adminRole] = await trx
.select() .select()
.from(roles) .from(roles)
.where(and(eq(roles.isAdmin, true), eq(roles.orgId, orgId))) .where(and(eq(roles.isAdmin, true), eq(roles.orgId, orgId)))
@@ -147,7 +140,7 @@ export async function calculateUserClientsForOrgs(
return existingClientCache.get(key) ?? null; return existingClientCache.get(key) ?? null;
} }
const [existingClient] = await transaction const [existingClient] = await trx
.select() .select()
.from(clients) .from(clients)
.where( .where(
@@ -164,16 +157,13 @@ export async function calculateUserClientsForOrgs(
return existingClient ?? null; return existingClient ?? null;
}; };
const hasRoleClientAccess = async ( const hasRoleClientAccess = async (roleId: number, clientId: number) => {
roleId: number,
clientId: number
) => {
const key = getRoleClientKey(roleId, clientId); const key = getRoleClientKey(roleId, clientId);
if (roleClientAccessCache.has(key)) { if (roleClientAccessCache.has(key)) {
return roleClientAccessCache.get(key)!; return roleClientAccessCache.get(key)!;
} }
const [existingRoleClient] = await transaction const [existingRoleClient] = await trx
.select() .select()
.from(roleClients) .from(roleClients)
.where( .where(
@@ -199,7 +189,7 @@ export async function calculateUserClientsForOrgs(
return userClientAccessCache.get(key)!; return userClientAccessCache.get(key)!;
} }
const [existingUserClient] = await transaction const [existingUserClient] = await trx
.select() .select()
.from(userClients) .from(userClients)
.where( .where(
@@ -217,7 +207,7 @@ export async function calculateUserClientsForOrgs(
}; };
// Get all OLMs for this user // Get all OLMs for this user
const userOlms = await transaction const userOlms = await trx
.select() .select()
.from(olms) .from(olms)
.where(eq(olms.userId, userId)); .where(eq(olms.userId, userId));
@@ -226,7 +216,7 @@ export async function calculateUserClientsForOrgs(
// No OLMs for this user, but we should still clean up any orphaned clients // No OLMs for this user, but we should still clean up any orphaned clients
await cleanupOrphanedClients( await cleanupOrphanedClients(
userId, userId,
transaction, trx,
[], [],
queuedAssociationRebuilds queuedAssociationRebuilds
); );
@@ -234,7 +224,7 @@ export async function calculateUserClientsForOrgs(
} }
// Get all user orgs with all roles (for org list and role-based logic) // Get all user orgs with all roles (for org list and role-based logic)
const userOrgRoleRows = await transaction const userOrgRoleRows = await trx
.select() .select()
.from(userOrgs) .from(userOrgs)
.innerJoin( .innerJoin(
@@ -250,10 +240,7 @@ export async function calculateUserClientsForOrgs(
const userOrgIds = [ const userOrgIds = [
...new Set(userOrgRoleRows.map((r) => r.userOrgs.orgId)) ...new Set(userOrgRoleRows.map((r) => r.userOrgs.orgId))
]; ];
const orgIdToRoleRows = new Map< const orgIdToRoleRows = new Map<string, (typeof userOrgRoleRows)[0][]>();
string,
(typeof userOrgRoleRows)[0][]
>();
for (const r of userOrgRoleRows) { for (const r of userOrgRoleRows) {
const list = orgIdToRoleRows.get(r.userOrgs.orgId) ?? []; const list = orgIdToRoleRows.get(r.userOrgs.orgId) ?? [];
list.push(r); list.push(r);
@@ -300,10 +287,7 @@ export async function calculateUserClientsForOrgs(
} }
// Check if a client already exists for this OLM+user+org combination // Check if a client already exists for this OLM+user+org combination
const existingClient = await getExistingClient( const existingClient = await getExistingClient(orgId, olm.olmId);
orgId,
olm.olmId
);
if (existingClient) { if (existingClient) {
// Ensure admin role has access to the client // Ensure admin role has access to the client
@@ -313,7 +297,7 @@ export async function calculateUserClientsForOrgs(
); );
if (!hasRoleAccess) { if (!hasRoleAccess) {
await transaction.insert(roleClients).values({ await trx.insert(roleClients).values({
roleId: adminRole.roleId, roleId: adminRole.roleId,
clientId: existingClient.clientId clientId: existingClient.clientId
}); });
@@ -336,7 +320,7 @@ export async function calculateUserClientsForOrgs(
); );
if (!hasUserAccess) { if (!hasUserAccess) {
await transaction.insert(userClients).values({ await trx.insert(userClients).values({
userId, userId,
clientId: existingClient.clientId clientId: existingClient.clientId
}); });
@@ -366,13 +350,11 @@ export async function calculateUserClientsForOrgs(
} }
const randomExitNode = const randomExitNode =
exitNodesList[ exitNodesList[Math.floor(Math.random() * exitNodesList.length)];
Math.floor(Math.random() * exitNodesList.length)
];
// Get next available subnet // Get next available subnet
const { value: newSubnet, release: releaseSubnetLock } = const { value: newSubnet, release: releaseSubnetLock } =
await getNextAvailableClientSubnet(orgId, transaction); await getNextAvailableClientSubnet(orgId, trx);
const subnet = newSubnet.split("/")[0]; const subnet = newSubnet.split("/")[0];
const updatedSubnet = `${subnet}/${org.subnet.split("/")[1]}`; const updatedSubnet = `${subnet}/${org.subnet.split("/")[1]}`;
@@ -398,19 +380,16 @@ export async function calculateUserClientsForOrgs(
}; };
// Create the client // Create the client
const [newClient] = await transaction const [newClient] = await trx
.insert(clients) .insert(clients)
.values(newClientData) .values(newClientData)
.returning(); .returning();
await releaseSubnetLock(); await releaseSubnetLock();
existingClientCache.set( existingClientCache.set(getOrgOlmKey(orgId, olm.olmId), newClient);
getOrgOlmKey(orgId, olm.olmId),
newClient
);
// create approval request // create approval request
if (requireApproval) { if (requireApproval) {
await transaction await trx
.insert(approvals) .insert(approvals)
.values({ .values({
timestamp: Math.floor(new Date().getTime() / 1000), timestamp: Math.floor(new Date().getTime() / 1000),
@@ -425,7 +404,7 @@ export async function calculateUserClientsForOrgs(
queuedAssociationRebuilds.push(newClient); queuedAssociationRebuilds.push(newClient);
// Grant admin role access to the client // Grant admin role access to the client
await transaction.insert(roleClients).values({ await trx.insert(roleClients).values({
roleId: adminRole.roleId, roleId: adminRole.roleId,
clientId: newClient.clientId clientId: newClient.clientId
}); });
@@ -435,7 +414,7 @@ export async function calculateUserClientsForOrgs(
); );
// Grant user access to the client // Grant user access to the client
await transaction.insert(userClients).values({ await trx.insert(userClients).values({
userId, userId,
clientId: newClient.clientId clientId: newClient.clientId
}); });
@@ -453,11 +432,10 @@ export async function calculateUserClientsForOrgs(
// Clean up clients in orgs the user is no longer in // Clean up clients in orgs the user is no longer in
await cleanupOrphanedClients( await cleanupOrphanedClients(
userId, userId,
transaction, trx,
userOrgIds, userOrgIds,
queuedAssociationRebuilds queuedAssociationRebuilds
); );
};
runQueuedClientAssociationRebuilds(userId, queuedAssociationRebuilds); runQueuedClientAssociationRebuilds(userId, queuedAssociationRebuilds);
} }
@@ -496,7 +474,7 @@ async function cleanupOrphanedClients(
) )
.returning(); .returning();
// Queue deleted clients for post-transaction association cleanup. // Queue deleted clients for post-trx association cleanup.
for (const deletedClient of deletedClients) { for (const deletedClient of deletedClients) {
queuedAssociationRebuilds.push(deletedClient); queuedAssociationRebuilds.push(deletedClient);