Update blueprints to support new clients

This commit is contained in:
Owen
2025-12-06 17:32:49 -05:00
parent 0beaadf512
commit 66fc8529c2
5 changed files with 578 additions and 282 deletions

View File

@@ -1,17 +1,23 @@
import {
clients,
clientSiteResources,
roles,
roleSiteResources,
SiteResource,
siteResources,
Transaction,
userOrgs,
users,
userSiteResources
} from "@server/db";
import { sites } from "@server/db";
import { eq, and } from "drizzle-orm";
import {
Config,
} from "./types";
import { eq, and, ne, inArray } from "drizzle-orm";
import { Config } from "./types";
import logger from "@server/logger";
export type ClientResourcesResults = {
resource: SiteResource;
newSiteResource: SiteResource;
oldSiteResource?: SiteResource;
}[];
export async function updateClientResources(
@@ -69,17 +75,22 @@ export async function updateClientResources(
}
if (existingResource) {
if (existingResource.siteId !== site.siteId) {
throw new Error(
`You can not change the site of an existing client resource (${resourceNiceId}). Please delete and recreate it instead.`
);
}
// Update existing resource
const [updatedResource] = await trx
.update(siteResources)
.set({
name: resourceData.name || resourceNiceId,
siteId: site.siteId,
mode: "port",
proxyPort: resourceData["proxy-port"]!,
destination: resourceData.hostname,
destinationPort: resourceData["internal-port"],
protocol: resourceData.protocol
mode: resourceData.mode,
destination: resourceData.destination,
enabled: true, // hardcoded for now
// enabled: resourceData.enabled ?? true,
alias: resourceData.alias || null
})
.where(
eq(
@@ -89,7 +100,110 @@ export async function updateClientResources(
)
.returning();
results.push({ resource: updatedResource });
const siteResourceId = existingResource.siteResourceId;
const orgId = existingResource.orgId;
await trx
.delete(clientSiteResources)
.where(eq(clientSiteResources.siteResourceId, siteResourceId));
if (resourceData.machines.length > 0) {
// get clientIds from niceIds
const clientsToUpdate = await trx
.select()
.from(clients)
.where(
and(
inArray(clients.niceId, resourceData.machines),
eq(clients.orgId, orgId)
)
);
const clientIds = clientsToUpdate.map(
(client) => client.clientId
);
await trx.insert(clientSiteResources).values(
clientIds.map((clientId) => ({
clientId,
siteResourceId
}))
);
}
await trx
.delete(userSiteResources)
.where(eq(userSiteResources.siteResourceId, siteResourceId));
if (resourceData.users.length > 0) {
// get userIds from username
const usersToUpdate = await trx
.select()
.from(users)
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
.where(
and(
inArray(users.username, resourceData.users),
eq(userOrgs.orgId, orgId)
)
);
const userIds = usersToUpdate.map((user) => user.user.userId);
await trx
.insert(userSiteResources)
.values(
userIds.map((userId) => ({ userId, siteResourceId }))
);
}
// Get all admin role IDs for this org to exclude from deletion
const adminRoles = await trx
.select()
.from(roles)
.where(and(eq(roles.isAdmin, true), eq(roles.orgId, orgId)));
const adminRoleIds = adminRoles.map((role) => role.roleId);
if (adminRoleIds.length > 0) {
await trx.delete(roleSiteResources).where(
and(
eq(roleSiteResources.siteResourceId, siteResourceId),
ne(roleSiteResources.roleId, adminRoleIds[0]) // delete all but the admin role
)
);
} else {
await trx
.delete(roleSiteResources)
.where(
eq(roleSiteResources.siteResourceId, siteResourceId)
);
}
if (resourceData.roles.length > 0) {
// Re-add specified roles but we need to get the roleIds from the role name in the array
const rolesToUpdate = await trx
.select()
.from(roles)
.where(
and(
eq(roles.orgId, orgId),
inArray(roles.name, resourceData.roles)
)
);
const roleIds = rolesToUpdate.map((role) => role.roleId);
await trx
.insert(roleSiteResources)
.values(
roleIds.map((roleId) => ({ roleId, siteResourceId }))
);
}
results.push({
newSiteResource: updatedResource,
oldSiteResource: existingResource
});
} else {
// Create new resource
const [newResource] = await trx
@@ -99,19 +213,103 @@ export async function updateClientResources(
siteId: site.siteId,
niceId: resourceNiceId,
name: resourceData.name || resourceNiceId,
mode: "port",
proxyPort: resourceData["proxy-port"]!,
destination: resourceData.hostname,
destinationPort: resourceData["internal-port"],
protocol: resourceData.protocol
mode: resourceData.mode,
destination: resourceData.destination,
enabled: true, // hardcoded for now
// enabled: resourceData.enabled ?? true,
alias: resourceData.alias || null
})
.returning();
const siteResourceId = newResource.siteResourceId;
const [adminRole] = await trx
.select()
.from(roles)
.where(and(eq(roles.isAdmin, true), eq(roles.orgId, orgId)))
.limit(1);
if (!adminRole) {
throw new Error(`Admin role not found for org ${orgId}`);
}
await trx.insert(roleSiteResources).values({
roleId: adminRole.roleId,
siteResourceId: siteResourceId
});
if (resourceData.roles.length > 0) {
// get roleIds from role names
const rolesToUpdate = await trx
.select()
.from(roles)
.where(
and(
eq(roles.orgId, orgId),
inArray(roles.name, resourceData.roles)
)
);
const roleIds = rolesToUpdate.map((role) => role.roleId);
await trx
.insert(roleSiteResources)
.values(
roleIds.map((roleId) => ({ roleId, siteResourceId }))
);
}
if (resourceData.users.length > 0) {
// get userIds from username
const usersToUpdate = await trx
.select()
.from(users)
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
.where(
and(
inArray(users.username, resourceData.users),
eq(userOrgs.orgId, orgId)
)
);
const userIds = usersToUpdate.map((user) => user.user.userId);
await trx
.insert(userSiteResources)
.values(
userIds.map((userId) => ({ userId, siteResourceId }))
);
}
if (resourceData.machines.length > 0) {
// get clientIds from niceIds
const clientsToUpdate = await trx
.select()
.from(clients)
.where(
and(
inArray(clients.niceId, resourceData.machines),
eq(clients.orgId, orgId)
)
);
const clientIds = clientsToUpdate.map(
(client) => client.clientId
);
await trx.insert(clientSiteResources).values(
clientIds.map((clientId) => ({
clientId,
siteResourceId
}))
);
}
logger.info(
`Created new client resource ${newResource.name} (${newResource.siteResourceId}) for org ${orgId}`
);
results.push({ resource: newResource });
results.push({ newSiteResource: newResource });
}
}