mirror of
https://github.com/fosrl/pangolin.git
synced 2026-06-27 01:29:14 +00:00
Compare commits
2 Commits
dependabot
...
1.19.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
784588cebc | ||
|
|
2e628fe0e4 |
@@ -1,4 +1,4 @@
|
|||||||
FROM node:26-alpine
|
FROM node:24-alpine
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user