Compare commits

..

17 Commits

Author SHA1 Message Date
miloschwartz ae0be3a4bc add resources overview column to pending sites table 2026-07-12 17:53:57 -04:00
miloschwartz a8f3f71021 add reject endpoint 2026-07-12 17:48:48 -04:00
miloschwartz 933ca71c16 add approve site endpoint 2026-07-12 17:16:08 -04:00
miloschwartz 591cb9cdc1 dont use strict query params in list alises 2026-07-10 18:05:16 -04:00
miloschwartz 34b18bdb53 only show approved resources in launcher 2026-07-10 17:24:10 -04:00
miloschwartz 7d475f5e91 filter out pending resources 2026-07-10 17:18:33 -04:00
miloschwartz 39c35fa539 reorganize components nad hooks for consistency 2026-07-10 17:05:45 -04:00
Owen e1bc0b7efd Make sure the enabled gets set to false 2026-07-10 15:36:05 -04:00
Owen 5ef068c8dc Set the resource from the site 2026-07-10 15:31:37 -04:00
Owen 94c01a23a9 Merge branch 'private-resource-enable' into provisioning-resources 2026-07-10 15:17:06 -04:00
Owen 609fb357bb Support enable in the blueprints 2026-07-10 15:16:50 -04:00
Owen cf9a17cc2e Add status filter 2026-07-10 15:04:28 -04:00
Owen 538b57941c Makes sure patch works
z#
2026-07-10 11:32:27 -04:00
Owen f4bee6406a Support partial updates 2026-07-10 10:11:47 -04:00
Owen 0b2693a317 Add toggle to the ui for enabled 2026-07-10 09:59:11 -04:00
Owen 3be2d928f6 Filter out disabled 2026-07-09 21:42:59 -04:00
Owen 8d018fe47d Clean up 2026-07-09 21:28:48 -04:00
124 changed files with 1248 additions and 2197 deletions
+9 -1
View File
@@ -449,8 +449,14 @@
"provisioningManage": "Provisioning", "provisioningManage": "Provisioning",
"provisioningDescription": "Manage provisioning keys and review pending sites awaiting approval.", "provisioningDescription": "Manage provisioning keys and review pending sites awaiting approval.",
"pendingSites": "Pending Sites", "pendingSites": "Pending Sites",
"siteApproveSuccess": "Site approved successfully", "siteApproveSuccess": "Site and associated resources approved successfully",
"siteApproveError": "Error approving site", "siteApproveError": "Error approving site",
"siteReject": "Reject Site",
"siteQuestionReject": "Are you sure you want to reject this site?",
"siteMessageReject": "This will permanently delete the site and any associated resources that are still pending.",
"siteConfirmReject": "Confirm Reject Site",
"siteRejectSuccess": "Site rejected successfully",
"siteRejectError": "Error rejecting site",
"provisioningKeys": "Provisioning Keys", "provisioningKeys": "Provisioning Keys",
"searchProvisioningKeys": "Search provisioning keys...", "searchProvisioningKeys": "Search provisioning keys...",
"provisioningKeysAdd": "Generate Provisioning Key", "provisioningKeysAdd": "Generate Provisioning Key",
@@ -1420,6 +1426,8 @@
"setupTokenDescription": "Enter the setup token from the server console.", "setupTokenDescription": "Enter the setup token from the server console.",
"setupTokenRequired": "Setup token is required", "setupTokenRequired": "Setup token is required",
"actionUpdateSite": "Update Site", "actionUpdateSite": "Update Site",
"actionApproveSite": "Approve Site",
"actionRejectSite": "Reject Site",
"actionResetSiteBandwidth": "Reset Organization Bandwidth", "actionResetSiteBandwidth": "Reset Organization Bandwidth",
"actionListSiteRoles": "List Allowed Site Roles", "actionListSiteRoles": "List Allowed Site Roles",
"actionCreateResource": "Create Resource", "actionCreateResource": "Create Resource",
+1
View File
@@ -21,6 +21,7 @@ export enum ActionsEnum {
getSite = "getSite", getSite = "getSite",
listSites = "listSites", listSites = "listSites",
updateSite = "updateSite", updateSite = "updateSite",
updateSiteApprovals = "updateSiteApprovals",
restartSite = "restartSite", restartSite = "restartSite",
resetSiteBandwidth = "resetSiteBandwidth", resetSiteBandwidth = "resetSiteBandwidth",
reGenerateSecret = "reGenerateSecret", reGenerateSecret = "reGenerateSecret",
+8 -2
View File
@@ -200,7 +200,10 @@ export const resources = pgTable(
authDaemonMode: varchar("authDaemonMode", { length: 32 }) authDaemonMode: varchar("authDaemonMode", { length: 32 })
.$type<"site" | "remote" | "native">() .$type<"site" | "remote" | "native">()
.default("site"), .default("site"),
authDaemonPort: integer("authDaemonPort").default(22123) authDaemonPort: integer("authDaemonPort").default(22123),
status: varchar("status")
.$type<"pending" | "approved">()
.default("approved")
}, },
(t) => [ (t) => [
index("idx_resources_fulldomain") index("idx_resources_fulldomain")
@@ -451,7 +454,10 @@ export const siteResources = pgTable(
onDelete: "set null" onDelete: "set null"
}), }),
subdomain: varchar("subdomain"), subdomain: varchar("subdomain"),
fullDomain: varchar("fullDomain") fullDomain: varchar("fullDomain"),
status: varchar("status")
.$type<"pending" | "approved">()
.default("approved")
}, },
(t) => [index("idx_siteresources_orgid_niceid").on(t.orgId, t.niceId)] (t) => [index("idx_siteresources_orgid_niceid").on(t.orgId, t.niceId)]
); );
+4 -2
View File
@@ -209,7 +209,8 @@ export const resources = sqliteTable("resources", {
authDaemonMode: text("authDaemonMode") authDaemonMode: text("authDaemonMode")
.$type<"site" | "remote" | "native">() .$type<"site" | "remote" | "native">()
.default("site"), .default("site"),
authDaemonPort: integer("authDaemonPort").default(22123) authDaemonPort: integer("authDaemonPort").default(22123),
status: text("status").$type<"pending" | "approved">().default("approved")
}); });
export const labels = sqliteTable("labels", { export const labels = sqliteTable("labels", {
@@ -447,7 +448,8 @@ export const siteResources = sqliteTable("siteResources", {
onDelete: "set null" onDelete: "set null"
}), }),
subdomain: text("subdomain"), subdomain: text("subdomain"),
fullDomain: text("fullDomain") fullDomain: text("fullDomain"),
status: text("status").$type<"pending" | "approved">().default("approved")
}); });
export const networks = sqliteTable("networks", { export const networks = sqliteTable("networks", {
+2 -3
View File
@@ -12,7 +12,7 @@ import { logIncomingMiddleware } from "./middlewares/logIncoming";
import helmet from "helmet"; import helmet from "helmet";
import swaggerUi from "swagger-ui-express"; import swaggerUi from "swagger-ui-express";
import { OpenApiGeneratorV3 } from "@asteasolutions/zod-to-openapi"; import { OpenApiGeneratorV3 } from "@asteasolutions/zod-to-openapi";
import { registry, openApiTags } from "./openApi"; import { registry } from "./openApi";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import { APP_PATH } from "./lib/consts"; import { APP_PATH } from "./lib/consts";
@@ -181,8 +181,7 @@ function getOpenApiDocumentation() {
version: "v1", version: "v1",
title: "Pangolin Integration API" title: "Pangolin Integration API"
}, },
servers: [{ url: "/v1" }], servers: [{ url: "/v1" }]
tags: openApiTags
}); });
if (!process.env.DISABLE_GEN_OPENAPI) { if (!process.env.DISABLE_GEN_OPENAPI) {
-6
View File
@@ -34,12 +34,6 @@ import {
rebuildClientAssociationsFromSiteResource, rebuildClientAssociationsFromSiteResource,
waitForSiteResourceRebuildIdle waitForSiteResourceRebuildIdle
} from "../rebuildClientAssociations"; } from "../rebuildClientAssociations";
import { build } from "@server/build";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import next from "next";
import { LimitId } from "../billing";
import { usageService } from "../billing/usageService";
type ApplyBlueprintArgs = { type ApplyBlueprintArgs = {
orgId: string; orgId: string;
+16 -10
View File
@@ -26,9 +26,6 @@ import { createCertificate } from "#dynamic/routers/certificates/createCertifica
import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed";
import { tierMatrix } from "../billing/tierMatrix"; import { tierMatrix } from "../billing/tierMatrix";
import { build } from "@server/build"; import { build } from "@server/build";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import next from "next";
import { LimitId } from "../billing"; import { LimitId } from "../billing";
import { usageService } from "../billing/usageService"; import { usageService } from "../billing/usageService";
@@ -201,17 +198,19 @@ export async function updatePrivateResources(
} }
} }
let resourceStatusFromSite: "approved" | "pending" = "approved";
if (siteId && allSites.length === 0) { if (siteId && allSites.length === 0) {
// only add if there are not provided sites // only add if there are not provided sites
// Use the provided siteId directly, but verify it belongs to the org // Use the provided siteId directly, but verify it belongs to the org
const [siteSingle] = await trx const [siteSingle] = await trx
.select({ siteId: sites.siteId }) .select({ siteId: sites.siteId, status: sites.status })
.from(sites) .from(sites)
.where(and(eq(sites.siteId, siteId), eq(sites.orgId, orgId))) .where(and(eq(sites.siteId, siteId), eq(sites.orgId, orgId)))
.limit(1); .limit(1);
if (siteSingle) { if (siteSingle) {
allSites.push(siteSingle); allSites.push(siteSingle);
} }
resourceStatusFromSite = siteSingle.status ?? "approved";
} }
if (allSites.length === 0) { if (allSites.length === 0) {
@@ -220,6 +219,13 @@ export async function updatePrivateResources(
); );
} }
const resourceEnabled =
resourceData.enabled == undefined || resourceData.enabled == null
? true
: resourceStatusFromSite === "pending"
? false
: resourceData.enabled;
if (existingResource) { if (existingResource) {
let domainInfo: let domainInfo:
| { subdomain: string | null; domainId: string } | { subdomain: string | null; domainId: string }
@@ -243,8 +249,7 @@ export async function updatePrivateResources(
scheme: resourceData.scheme, scheme: resourceData.scheme,
destination: resourceData.destination, destination: resourceData.destination,
destinationPort: resourceData["destination-port"], destinationPort: resourceData["destination-port"],
enabled: true, // hardcoded for now enabled: resourceEnabled,
// enabled: resourceData.enabled ?? true,
alias: resourceData.alias || null, alias: resourceData.alias || null,
disableIcmp: disableIcmp:
resourceData["disable-icmp"] || resourceData["disable-icmp"] ||
@@ -263,7 +268,8 @@ export async function updatePrivateResources(
pamMode: resourceData["auth-daemon"]?.pam || "passthrough", pamMode: resourceData["auth-daemon"]?.pam || "passthrough",
authDaemonMode: authDaemonMode:
resourceData["auth-daemon"]?.mode || "native", resourceData["auth-daemon"]?.mode || "native",
authDaemonPort: resourceData["auth-daemon"]?.port || 22123 authDaemonPort: resourceData["auth-daemon"]?.port || 22123,
status: resourceStatusFromSite
}) })
.where( .where(
eq( eq(
@@ -496,8 +502,7 @@ export async function updatePrivateResources(
scheme: resourceData.scheme, scheme: resourceData.scheme,
destination: resourceData.destination, destination: resourceData.destination,
destinationPort: resourceData["destination-port"], destinationPort: resourceData["destination-port"],
enabled: true, // hardcoded for now enabled: resourceEnabled,
// enabled: resourceData.enabled ?? true,
alias: resourceData.alias || null, alias: resourceData.alias || null,
aliasAddress: aliasAddress, aliasAddress: aliasAddress,
disableIcmp: disableIcmp:
@@ -517,7 +522,8 @@ export async function updatePrivateResources(
pamMode: resourceData["auth-daemon"]?.pam || "passthrough", pamMode: resourceData["auth-daemon"]?.pam || "passthrough",
authDaemonMode: authDaemonMode:
resourceData["auth-daemon"]?.mode || "native", resourceData["auth-daemon"]?.mode || "native",
authDaemonPort: resourceData["auth-daemon"]?.port || 22123 authDaemonPort: resourceData["auth-daemon"]?.port || 22123,
status: resourceStatusFromSite
}) })
.returning(); .returning();
+37 -15
View File
@@ -25,6 +25,7 @@ import {
rolePolicies, rolePolicies,
roleResources, roleResources,
roles, roles,
Site,
sites, sites,
Target, Target,
TargetHealthCheck, TargetHealthCheck,
@@ -74,19 +75,40 @@ export async function updatePublicResources(
)) { )) {
const targetsToUpdate: Target[] = []; const targetsToUpdate: Target[] = [];
const healthchecksToUpdate: TargetHealthCheck[] = []; const healthchecksToUpdate: TargetHealthCheck[] = [];
let resource: Resource; let resource: Resource;
let resourceStatusFromSite: "approved" | "pending" = "approved";
let providedSite: Partial<Site> | undefined;
if (siteId) {
// Use the provided siteId directly, but verify it belongs to the org
[providedSite] = await trx
.select({
siteId: sites.siteId,
type: sites.type,
status: sites.status
})
.from(sites)
.where(and(eq(sites.siteId, siteId), eq(sites.orgId, orgId)))
.limit(1);
resourceStatusFromSite = providedSite?.status ?? "approved";
}
async function createTarget( // reusable function to create a target async function createTarget( // reusable function to create a target
resourceId: number, resourceId: number,
targetData: TargetData targetData: TargetData
) { ) {
const targetSiteId = targetData.site; const targetSiteId = targetData.site;
let site; let site: Partial<Site> | undefined;
if (targetSiteId) { if (targetSiteId) {
// Look up site by niceId // Look up site by niceId
[site] = await trx [site] = await trx
.select({ siteId: sites.siteId, type: sites.type }) .select({
siteId: sites.siteId,
type: sites.type,
status: sites.status
})
.from(sites) .from(sites)
.where( .where(
and( and(
@@ -95,15 +117,9 @@ export async function updatePublicResources(
) )
) )
.limit(1); .limit(1);
} else if (siteId) { } else if (siteId && providedSite) {
// Use the provided siteId directly, but verify it belongs to the org // Use the provided siteId directly, but verify it belongs to the org
[site] = await trx site = providedSite;
.select({ siteId: sites.siteId, type: sites.type })
.from(sites)
.where(
and(eq(sites.siteId, siteId), eq(sites.orgId, orgId))
)
.limit(1);
} else { } else {
throw new Error(`Target site is required`); throw new Error(`Target site is required`);
} }
@@ -139,7 +155,7 @@ export async function updatePublicResources(
.insert(targets) .insert(targets)
.values({ .values({
resourceId: resourceId, resourceId: resourceId,
siteId: site.siteId, siteId: site.siteId!,
ip: targetData.hostname, ip: targetData.hostname,
mode: resourceData.mode as Target["mode"], mode: resourceData.mode as Target["mode"],
method: targetData.method, method: targetData.method,
@@ -172,7 +188,7 @@ export async function updatePublicResources(
.insert(targetHealthCheck) .insert(targetHealthCheck)
.values({ .values({
name: `${targetData.hostname}:${targetData.port}`, name: `${targetData.hostname}:${targetData.port}`,
siteId: site.siteId, siteId: site.siteId!,
targetId: newTarget.targetId, targetId: newTarget.targetId,
orgId: orgId, orgId: orgId,
hcEnabled: healthcheckData?.enabled || false, hcEnabled: healthcheckData?.enabled || false,
@@ -230,7 +246,10 @@ export async function updatePublicResources(
const resourceEnabled = const resourceEnabled =
resourceData.enabled == undefined || resourceData.enabled == null resourceData.enabled == undefined || resourceData.enabled == null
? true ? true
: resourceData.enabled; : resourceStatusFromSite === "pending"
? false
: resourceData.enabled;
const resourceSsl = const resourceSsl =
resourceData.ssl == undefined || resourceData.ssl == null resourceData.ssl == undefined || resourceData.ssl == null
? true ? true
@@ -406,7 +425,8 @@ export async function updatePublicResources(
? (resourceData["proxy-protocol-version"] ?? ? (resourceData["proxy-protocol-version"] ??
1) 1)
: 1, : 1,
resourcePolicyId: sharedPolicy.resourcePolicyId resourcePolicyId: sharedPolicy.resourcePolicyId,
status: resourceStatusFromSite
}) })
.where( .where(
eq( eq(
@@ -590,7 +610,8 @@ export async function updatePublicResources(
authDaemonPort: authDaemonPort:
resourceData["auth-daemon"]?.port || 22123, resourceData["auth-daemon"]?.port || 22123,
resourcePolicyId: null, resourcePolicyId: null,
defaultResourcePolicyId: inlinePolicyId defaultResourcePolicyId: inlinePolicyId,
status: resourceStatusFromSite
}) })
.where( .where(
eq( eq(
@@ -1131,6 +1152,7 @@ export async function updatePublicResources(
.values({ .values({
orgId, orgId,
niceId: resourceNiceId, niceId: resourceNiceId,
status: resourceStatusFromSite,
name: resourceData.name || "Unnamed Resource", name: resourceData.name || "Unnamed Resource",
mode: resourceData.mode, mode: resourceData.mode,
proxyPort: ["http", "ssh", "rdp", "vnc"].includes( proxyPort: ["http", "ssh", "rdp", "vnc"].includes(
+1 -1
View File
@@ -470,7 +470,7 @@ export const PrivateResourceSchema = z
// proxyPort: z.int().positive().optional(), // proxyPort: z.int().positive().optional(),
"destination-port": z.int().positive().optional(), "destination-port": z.int().positive().optional(),
destination: z.string().min(1).optional(), destination: z.string().min(1).optional(),
// enabled: z.boolean().default(true), enabled: z.boolean().default(true),
"tcp-ports": portRangeStringSchema.optional().default("*"), "tcp-ports": portRangeStringSchema.optional().default("*"),
"udp-ports": portRangeStringSchema.optional().default("*"), "udp-ports": portRangeStringSchema.optional().default("*"),
"disable-icmp": z.boolean().optional().default(false), "disable-icmp": z.boolean().optional().default(false),
+3 -5
View File
@@ -14,8 +14,6 @@ import {
} from "@server/db"; } from "@server/db";
import logger from "@server/logger"; import logger from "@server/logger";
import { removeTargets } from "@server/routers/newt/targets"; import { removeTargets } from "@server/routers/newt/targets";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";
export type DeleteResourceResult = { export type DeleteResourceResult = {
deletedResource: Resource; deletedResource: Resource;
@@ -117,10 +115,10 @@ export async function runResourceDeleteSideEffects(
.limit(1); .limit(1);
if (!site) { if (!site) {
throw createHttpError( logger.debug(
HttpCode.NOT_FOUND, `Site with ID ${target.siteId} not found during resource delete side effects; skipping target removal`
`Site with ID ${target.siteId} not found`
); );
continue;
} }
if (site.pubKey && site.type === "newt") { if (site.pubKey && site.type === "newt") {
+82 -3
View File
@@ -1,6 +1,7 @@
import { and, eq, sql } from "drizzle-orm"; import { and, eq, inArray, sql } from "drizzle-orm";
import { import {
db, db,
resources,
siteNetworks, siteNetworks,
siteResources, siteResources,
targets, targets,
@@ -97,6 +98,64 @@ export function exceedsSiteAssociatedResourceDeleteLimit(
return resourceCount > MAX_SITE_ASSOCIATED_RESOURCES_FOR_BULK_DELETE; return resourceCount > MAX_SITE_ASSOCIATED_RESOURCES_FOR_BULK_DELETE;
} }
export async function getPendingResourceIdsForSite(
siteId: number,
trx: Transaction | typeof db = db
): Promise<number[]> {
const resourceIds = await getResourceIdsForSite(siteId, trx);
if (resourceIds.length === 0) {
return [];
}
const rows = await trx
.select({ resourceId: resources.resourceId })
.from(resources)
.where(
and(
inArray(resources.resourceId, resourceIds),
eq(resources.status, "pending")
)
);
return rows.map((row) => row.resourceId);
}
export async function getPendingSiteResourceIdsForSite(
siteId: number,
orgId: string,
trx: Transaction | typeof db = db
): Promise<number[]> {
const siteResourceIds = await getSiteResourceIdsForSite(siteId, orgId, trx);
if (siteResourceIds.length === 0) {
return [];
}
const rows = await trx
.select({ siteResourceId: siteResources.siteResourceId })
.from(siteResources)
.where(
and(
inArray(siteResources.siteResourceId, siteResourceIds),
eq(siteResources.status, "pending")
)
);
return rows.map((row) => row.siteResourceId);
}
export async function getPendingAssociatedResourceCountForSite(
siteId: number,
orgId: string,
trx: Transaction | typeof db = db
): Promise<number> {
const [resourceIds, siteResourceIds] = await Promise.all([
getPendingResourceIdsForSite(siteId, trx),
getPendingSiteResourceIdsForSite(siteId, orgId, trx)
]);
return resourceIds.length + siteResourceIds.length;
}
export async function deleteAssociatedResourcesForSite( export async function deleteAssociatedResourcesForSite(
siteId: number, siteId: number,
orgId: string, orgId: string,
@@ -105,12 +164,32 @@ export async function deleteAssociatedResourcesForSite(
const resourceIds = await getResourceIdsForSite(siteId, trx); const resourceIds = await getResourceIdsForSite(siteId, trx);
const siteResourceIds = await getSiteResourceIdsForSite(siteId, orgId, trx); const siteResourceIds = await getSiteResourceIdsForSite(siteId, orgId, trx);
const [resources, siteResourcesDeleted] = await Promise.all([ const [deletedResources, siteResourcesDeleted] = await Promise.all([
performDeleteResources(resourceIds, trx), performDeleteResources(resourceIds, trx),
performDeleteSiteResources(siteResourceIds, trx) performDeleteSiteResources(siteResourceIds, trx)
]); ]);
return { resources, siteResources: siteResourcesDeleted }; return { resources: deletedResources, siteResources: siteResourcesDeleted };
}
export async function deletePendingAssociatedResourcesForSite(
siteId: number,
orgId: string,
trx: Transaction | typeof db = db
): Promise<DeleteSiteAssociatedResourcesSideEffects> {
const resourceIds = await getPendingResourceIdsForSite(siteId, trx);
const siteResourceIds = await getPendingSiteResourceIdsForSite(
siteId,
orgId,
trx
);
const [deletedResources, siteResourcesDeleted] = await Promise.all([
performDeleteResources(resourceIds, trx),
performDeleteSiteResources(siteResourceIds, trx)
]);
return { resources: deletedResources, siteResources: siteResourcesDeleted };
} }
export async function runDeleteSiteAssociatedResourcesSideEffects( export async function runDeleteSiteAssociatedResourcesSideEffects(
+9
View File
@@ -496,6 +496,7 @@ export function generateRemoteSubnets(
): string[] { ): string[] {
const remoteSubnets = allSiteResources const remoteSubnets = allSiteResources
.filter((sr) => { .filter((sr) => {
if (!sr.enabled) return false;
if (!sr.destination) return false; if (!sr.destination) return false;
if (sr.mode === "cidr") { if (sr.mode === "cidr") {
@@ -530,6 +531,7 @@ export function generateAliasConfig(allSiteResources: SiteResource[]): Alias[] {
return allSiteResources return allSiteResources
.filter( .filter(
(sr) => (sr) =>
sr.enabled &&
sr.aliasAddress && sr.aliasAddress &&
((sr.alias && (sr.mode == "host" || sr.mode == "ssh")) || ((sr.alias && (sr.mode == "host" || sr.mode == "ssh")) ||
(sr.fullDomain && sr.mode == "http")) (sr.fullDomain && sr.mode == "http"))
@@ -662,6 +664,13 @@ export async function generateSubnetProxyTargetV2(
subnet: string | null; subnet: string | null;
}[] }[]
): Promise<SubnetProxyTargetV2[] | undefined> { ): Promise<SubnetProxyTargetV2[] | undefined> {
if (!siteResource.enabled) {
logger.debug(
`Site resource ${siteResource.siteResourceId} is disabled, skipping target generation.`
);
return;
}
if (clients.length === 0) { if (clients.length === 0) {
logger.debug( logger.debug(
`No clients have access to site resource ${siteResource.siteResourceId}, skipping target generation.` `No clients have access to site resource ${siteResource.siteResourceId}, skipping target generation.`
+30 -16
View File
@@ -1561,9 +1561,19 @@ export async function handleMessagingForUpdatedSiteResource(
updatedSiteResource.udpPortRangeString || updatedSiteResource.udpPortRangeString ||
existingSiteResource.disableIcmp !== existingSiteResource.disableIcmp !==
updatedSiteResource.disableIcmp); updatedSiteResource.disableIcmp);
// Toggling enabled on/off doesn't change any of the fields above, but it
// does change whether targets/peer data should exist at all, so it needs
// to drive the same old->new diff machinery: going enabled->disabled
// diffs "real data" against "nothing" (a remove), and disabled->enabled
// diffs "nothing" against "real data" (an add). generateSubnetProxyTargetV2/
// generateRemoteSubnets/generateAliasConfig already return nothing for a
// disabled resource, so no other changes are needed here.
const enabledChanged =
existingSiteResource &&
existingSiteResource.enabled !== updatedSiteResource.enabled;
logger.debug( logger.debug(
`handleMessagingForUpdatedSiteResource: change flags destinationChanged=${Boolean(destinationChanged)} destinationPortChanged=${Boolean(destinationPortChanged)} aliasChanged=${Boolean(aliasChanged)} fullDomainChanged=${Boolean(fullDomainChanged)} sslChanged=${Boolean(sslChanged)} portRangesChanged=${Boolean(portRangesChanged)}` `handleMessagingForUpdatedSiteResource: change flags destinationChanged=${Boolean(destinationChanged)} destinationPortChanged=${Boolean(destinationPortChanged)} aliasChanged=${Boolean(aliasChanged)} fullDomainChanged=${Boolean(fullDomainChanged)} sslChanged=${Boolean(sslChanged)} portRangesChanged=${Boolean(portRangesChanged)} enabledChanged=${Boolean(enabledChanged)}`
); );
// if the existingSiteResource is undefined (new resource) we don't need to do anything here, the rebuild above handled it all // if the existingSiteResource is undefined (new resource) we don't need to do anything here, the rebuild above handled it all
@@ -1574,14 +1584,16 @@ export async function handleMessagingForUpdatedSiteResource(
fullDomainChanged || fullDomainChanged ||
sslChanged || sslChanged ||
portRangesChanged || portRangesChanged ||
destinationPortChanged destinationPortChanged ||
enabledChanged
) { ) {
const shouldUpdateTargets = const shouldUpdateTargets =
destinationChanged || destinationChanged ||
sslChanged || sslChanged ||
portRangesChanged || portRangesChanged ||
fullDomainChanged || fullDomainChanged ||
destinationPortChanged; destinationPortChanged ||
enabledChanged;
logger.debug( logger.debug(
`handleMessagingForUpdatedSiteResource: entering unchanged-site update path shouldUpdateTargets=${shouldUpdateTargets}` `handleMessagingForUpdatedSiteResource: entering unchanged-site update path shouldUpdateTargets=${shouldUpdateTargets}`
@@ -1657,20 +1669,22 @@ export async function handleMessagingForUpdatedSiteResource(
peerDataUpdateBatch.push({ peerDataUpdateBatch.push({
clientId: client.clientId, clientId: client.clientId,
siteId, siteId,
remoteSubnets: destinationChanged remoteSubnets:
? { destinationChanged || enabledChanged
oldRemoteSubnets: !oldDestinationStillInUseBySite ? {
? generateRemoteSubnets([ oldRemoteSubnets:
existingSiteResource !oldDestinationStillInUseBySite
]) ? generateRemoteSubnets([
: [], existingSiteResource
newRemoteSubnets: generateRemoteSubnets([ ])
updatedSiteResource : [],
]) newRemoteSubnets: generateRemoteSubnets([
} updatedSiteResource
: undefined, ])
}
: undefined,
aliases: aliases:
aliasChanged || fullDomainChanged // the full domain is sent down as an alias aliasChanged || fullDomainChanged || enabledChanged // the full domain is sent down as an alias
? { ? {
oldAliases: generateAliasConfig([ oldAliases: generateAliasConfig([
existingSiteResource existingSiteResource
+8 -19
View File
@@ -4,33 +4,22 @@ export const registry = new OpenAPIRegistry();
export enum OpenAPITags { export enum OpenAPITags {
Site = "Site", Site = "Site",
PublicResource = "Public Resource",
Target = "Resource Target",
PrivateResource = "Private Resource",
Client = "Client",
Org = "Organization", Org = "Organization",
Domain = "Domain", PublicResource = "Public Resource",
PublicResourcePolicy = "Public Resource Policy", PrivateResource = "Private Resource",
Policy = "Policy",
Role = "Role", Role = "Role",
User = "User", User = "User",
Rule = "Rule",
Invitation = "User Invitation", Invitation = "User Invitation",
Target = "Resource Target",
Rule = "Rule",
AccessToken = "Access Token", AccessToken = "Access Token",
GlobalIdp = "Identity Provider (Global)", GlobalIdp = "Identity Provider (Global)",
OrgIdp = "Identity Provider (Organization Only)", OrgIdp = "Identity Provider (Organization Only)",
Client = "Client",
ApiKey = "API Key", ApiKey = "API Key",
Domain = "Domain",
Blueprint = "Blueprint", Blueprint = "Blueprint",
Ssh = "SSH", Ssh = "SSH",
Logs = "Logs", Logs = "Logs"
EventStreamingDestination = "Event Streaming Destination",
AlertRule = "Alert Rule",
HealthCheck = "Health Check",
PublicResourcePolicyLegacy = "Public Resource Policy (Legacy)",
PublicResourceLegacy = "Public Resource (Legacy)",
PrivateResourceLegacy = "Private Resource (Legacy)"
} }
// Order here controls the order tags are displayed in Swagger UI
export const openApiTags = Object.values(OpenAPITags).map((name) => ({
name
}));
@@ -191,7 +191,7 @@ registry.registerPath({
method: "put", method: "put",
path: "/org/{orgId}/alert-rule", path: "/org/{orgId}/alert-rule",
description: "Create an alert rule for a specific organization.", description: "Create an alert rule for a specific organization.",
tags: [OpenAPITags.AlertRule], tags: [OpenAPITags.Org],
request: { request: {
params: paramsSchema, params: paramsSchema,
body: { body: {
@@ -34,7 +34,7 @@ registry.registerPath({
method: "delete", method: "delete",
path: "/org/{orgId}/alert-rule/{alertRuleId}", path: "/org/{orgId}/alert-rule/{alertRuleId}",
description: "Delete an alert rule for a specific organization.", description: "Delete an alert rule for a specific organization.",
tags: [OpenAPITags.AlertRule], tags: [OpenAPITags.Org],
request: { request: {
params: paramsSchema params: paramsSchema
}, },
@@ -48,7 +48,7 @@ registry.registerPath({
method: "get", method: "get",
path: "/org/{orgId}/alert-rule/{alertRuleId}", path: "/org/{orgId}/alert-rule/{alertRuleId}",
description: "Get a specific alert rule for an organization.", description: "Get a specific alert rule for an organization.",
tags: [OpenAPITags.AlertRule], tags: [OpenAPITags.Org],
request: { request: {
params: paramsSchema params: paramsSchema
}, },
@@ -90,7 +90,7 @@ registry.registerPath({
method: "get", method: "get",
path: "/org/{orgId}/alert-rules", path: "/org/{orgId}/alert-rules",
description: "List all alert rules for a specific organization.", description: "List all alert rules for a specific organization.",
tags: [OpenAPITags.AlertRule], tags: [OpenAPITags.Org],
request: { request: {
query: querySchema, query: querySchema,
params: paramsSchema params: paramsSchema
@@ -158,7 +158,7 @@ registry.registerPath({
method: "post", method: "post",
path: "/org/{orgId}/alert-rule/{alertRuleId}", path: "/org/{orgId}/alert-rule/{alertRuleId}",
description: "Update an alert rule for a specific organization.", description: "Update an alert rule for a specific organization.",
tags: [OpenAPITags.AlertRule], tags: [OpenAPITags.Org],
request: { request: {
params: paramsSchema, params: paramsSchema,
body: { body: {
@@ -52,7 +52,7 @@ registry.registerPath({
method: "put", method: "put",
path: "/org/{orgId}/event-streaming-destination", path: "/org/{orgId}/event-streaming-destination",
description: "Create an event streaming destination for a specific organization.", description: "Create an event streaming destination for a specific organization.",
tags: [OpenAPITags.EventStreamingDestination], tags: [OpenAPITags.Org],
request: { request: {
params: paramsSchema, params: paramsSchema,
body: { body: {
@@ -35,7 +35,7 @@ registry.registerPath({
path: "/org/{orgId}/event-streaming-destination/{destinationId}", path: "/org/{orgId}/event-streaming-destination/{destinationId}",
description: description:
"Delete an event streaming destination for a specific organization.", "Delete an event streaming destination for a specific organization.",
tags: [OpenAPITags.EventStreamingDestination], tags: [OpenAPITags.Org],
request: { request: {
params: paramsSchema params: paramsSchema
}, },
@@ -109,7 +109,7 @@ registry.registerPath({
path: "/org/{orgId}/event-streaming-destination", path: "/org/{orgId}/event-streaming-destination",
description: description:
"List all event streaming destinations for a specific organization.", "List all event streaming destinations for a specific organization.",
tags: [OpenAPITags.EventStreamingDestination], tags: [OpenAPITags.Org],
request: { request: {
query: querySchema, query: querySchema,
params: paramsSchema params: paramsSchema
@@ -55,7 +55,7 @@ registry.registerPath({
method: "post", method: "post",
path: "/org/{orgId}/event-streaming-destination/{destinationId}", path: "/org/{orgId}/event-streaming-destination/{destinationId}",
description: "Update an event streaming destination for a specific organization.", description: "Update an event streaming destination for a specific organization.",
tags: [OpenAPITags.EventStreamingDestination], tags: [OpenAPITags.Org],
request: { request: {
params: paramsSchema, params: paramsSchema,
body: { body: {
@@ -75,7 +75,7 @@ registry.registerPath({
method: "put", method: "put",
path: "/org/{orgId}/health-check", path: "/org/{orgId}/health-check",
description: "Create a health check for a specific organization.", description: "Create a health check for a specific organization.",
tags: [OpenAPITags.HealthCheck], tags: [OpenAPITags.Org],
request: { request: {
params: paramsSchema, params: paramsSchema,
body: { body: {
@@ -37,7 +37,7 @@ registry.registerPath({
method: "delete", method: "delete",
path: "/org/{orgId}/health-check/{healthCheckId}", path: "/org/{orgId}/health-check/{healthCheckId}",
description: "Delete a health check for a specific organization.", description: "Delete a health check for a specific organization.",
tags: [OpenAPITags.HealthCheck], tags: [OpenAPITags.Org],
request: { request: {
params: paramsSchema params: paramsSchema
}, },
@@ -63,7 +63,7 @@ registry.registerPath({
method: "get", method: "get",
path: "/org/{orgId}/health-checks", path: "/org/{orgId}/health-checks",
description: "List health checks for an organization.", description: "List health checks for an organization.",
tags: [OpenAPITags.HealthCheck], tags: [OpenAPITags.Org],
request: { request: {
params: paramsSchema, params: paramsSchema,
query: querySchema query: querySchema
@@ -109,7 +109,7 @@ registry.registerPath({
method: "post", method: "post",
path: "/org/{orgId}/health-check/{healthCheckId}", path: "/org/{orgId}/health-check/{healthCheckId}",
description: "Update a health check for a specific organization.", description: "Update a health check for a specific organization.",
tags: [OpenAPITags.HealthCheck], tags: [OpenAPITags.Org],
request: { request: {
params: paramsSchema, params: paramsSchema,
body: { body: {
-149
View File
@@ -16,10 +16,6 @@ import * as org from "#private/routers/org";
import * as logs from "#private/routers/auditLogs"; import * as logs from "#private/routers/auditLogs";
import * as alertEvents from "#private/routers/alertEvents"; import * as alertEvents from "#private/routers/alertEvents";
import * as certificates from "#private/routers/certificates"; import * as certificates from "#private/routers/certificates";
import * as policy from "#private/routers/policy";
import * as eventStreamingDestination from "#private/routers/eventStreamingDestination";
import * as alertRule from "#private/routers/alertRule";
import * as healthChecks from "#private/routers/healthChecks";
import { import {
verifyApiKeyHasAction, verifyApiKeyHasAction,
@@ -28,7 +24,6 @@ import {
verifyApiKeyIdpAccess, verifyApiKeyIdpAccess,
verifyApiKeyRoleAccess, verifyApiKeyRoleAccess,
verifyApiKeyUserAccess, verifyApiKeyUserAccess,
verifyApiKeyResourcePolicyAccess,
verifyLimits verifyLimits
} from "@server/middlewares"; } from "@server/middlewares";
import * as user from "#private/routers/user"; import * as user from "#private/routers/user";
@@ -220,147 +215,3 @@ authenticated.delete(
logActionAudit(ActionsEnum.removeUserRole), logActionAudit(ActionsEnum.removeUserRole),
user.removeUserRole user.removeUserRole
); );
authenticated.get(
["/org/:orgId/resource-policies", "/org/:orgId/public-resource-policies"],
verifyValidLicense,
verifyValidSubscription(tierMatrix.resourcePolicies),
verifyApiKeyOrgAccess,
verifyLimits,
verifyApiKeyHasAction(ActionsEnum.listResourcePolicies),
logActionAudit(ActionsEnum.listResourcePolicies),
policy.listResourcePolicies
);
authenticated.post(
["/org/:orgId/resource-policy", "/org/:orgId/public-resource-policy"],
verifyValidLicense,
verifyValidSubscription(tierMatrix.resourcePolicies),
verifyApiKeyOrgAccess,
verifyLimits,
verifyApiKeyHasAction(ActionsEnum.createResourcePolicy),
logActionAudit(ActionsEnum.createResourcePolicy),
policy.createResourcePolicy
);
authenticated.delete(
["/resource-policy/:resourcePolicyId", "/public-resource-policy/:resourcePolicyId"],
verifyApiKeyResourcePolicyAccess,
verifyValidLicense,
verifyValidSubscription(tierMatrix.resourcePolicies),
verifyLimits,
verifyApiKeyHasAction(ActionsEnum.deleteResourcePolicy),
logActionAudit(ActionsEnum.deleteResourcePolicy),
policy.deleteResourcePolicy
);
authenticated.put(
"/org/:orgId/event-streaming-destination",
verifyApiKeyOrgAccess,
verifyLimits,
verifyApiKeyHasAction(ActionsEnum.createEventStreamingDestination),
logActionAudit(ActionsEnum.createEventStreamingDestination),
eventStreamingDestination.createEventStreamingDestination
);
authenticated.post(
"/org/:orgId/event-streaming-destination/:destinationId",
verifyApiKeyOrgAccess,
verifyLimits,
verifyApiKeyHasAction(ActionsEnum.updateEventStreamingDestination),
logActionAudit(ActionsEnum.updateEventStreamingDestination),
eventStreamingDestination.updateEventStreamingDestination
);
authenticated.delete(
"/org/:orgId/event-streaming-destination/:destinationId",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.deleteEventStreamingDestination),
logActionAudit(ActionsEnum.deleteEventStreamingDestination),
eventStreamingDestination.deleteEventStreamingDestination
);
authenticated.get(
"/org/:orgId/event-streaming-destinations",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.listEventStreamingDestinations),
eventStreamingDestination.listEventStreamingDestinations
);
authenticated.put(
"/org/:orgId/alert-rule",
verifyApiKeyOrgAccess,
verifyLimits,
verifyApiKeyHasAction(ActionsEnum.createAlertRule),
logActionAudit(ActionsEnum.createAlertRule),
alertRule.createAlertRule
);
authenticated.post(
"/org/:orgId/alert-rule/:alertRuleId",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.updateAlertRule),
logActionAudit(ActionsEnum.updateAlertRule),
alertRule.updateAlertRule
);
authenticated.delete(
"/org/:orgId/alert-rule/:alertRuleId",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.deleteAlertRule),
logActionAudit(ActionsEnum.deleteAlertRule),
alertRule.deleteAlertRule
);
authenticated.get(
"/org/:orgId/alert-rules",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.listAlertRules),
alertRule.listAlertRules
);
authenticated.get(
"/org/:orgId/alert-rule/:alertRuleId",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.getAlertRule),
alertRule.getAlertRule
);
authenticated.get(
"/org/:orgId/health-checks",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.listHealthChecks),
healthChecks.listHealthChecks
);
authenticated.put(
"/org/:orgId/health-check",
verifyApiKeyOrgAccess,
verifyLimits,
verifyApiKeyHasAction(ActionsEnum.createHealthCheck),
logActionAudit(ActionsEnum.createHealthCheck),
healthChecks.createHealthCheck
);
authenticated.post(
"/org/:orgId/health-check/:healthCheckId",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.updateHealthCheck),
logActionAudit(ActionsEnum.updateHealthCheck),
healthChecks.updateHealthCheck
);
authenticated.delete(
"/org/:orgId/health-check/:healthCheckId",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.deleteHealthCheck),
logActionAudit(ActionsEnum.deleteHealthCheck),
healthChecks.deleteHealthCheck
);
authenticated.get(
"/org/:orgId/health-check/:healthCheckId/status-history",
verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.getTarget),
healthChecks.getHealthCheckStatusHistory
);
@@ -121,7 +121,7 @@ registry.registerPath({
method: "post", method: "post",
path: "/org/{orgId}/resource-policy", path: "/org/{orgId}/resource-policy",
description: "Create a resource policy.", description: "Create a resource policy.",
tags: [OpenAPITags.PublicResourcePolicy], tags: [OpenAPITags.Org, OpenAPITags.Policy],
request: { request: {
params: createResourcePolicyParamsSchema, params: createResourcePolicyParamsSchema,
body: { body: {
@@ -31,7 +31,7 @@ registry.registerPath({
method: "delete", method: "delete",
path: "/resource-policy/{resourcePolicyId}", path: "/resource-policy/{resourcePolicyId}",
description: "Delete a resource policy.", description: "Delete a resource policy.",
tags: [OpenAPITags.PublicResourcePolicy], tags: [OpenAPITags.Policy],
request: { request: {
params: deleteResourcePolicySchema params: deleteResourcePolicySchema
}, },
@@ -79,7 +79,7 @@ registry.registerPath({
method: "get", method: "get",
path: "/org/{orgId}/resource-policies", path: "/org/{orgId}/resource-policies",
description: "List resource policies for an organization.", description: "List resource policies for an organization.",
tags: [OpenAPITags.PublicResourcePolicy], tags: [OpenAPITags.Org, OpenAPITags.Policy],
request: { request: {
params: z.object({ params: z.object({
orgId: z.string() orgId: z.string()
@@ -44,39 +44,6 @@ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/access-token", path: "/resource/{resourceId}/access-token",
description: "Generate a new access token for a resource.", description: "Generate a new access token for a resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: generateAccssTokenParamsSchema,
body: {
content: {
"application/json": {
schema: generateAccessTokenBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/access-token",
description: "Generate a new access token for a resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.AccessToken], tags: [OpenAPITags.PublicResource, OpenAPITags.AccessToken],
request: { request: {
params: generateAccssTokenParamsSchema, params: generateAccssTokenParamsSchema,
@@ -151,35 +151,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/resource/{resourceId}/access-tokens", path: "/resource/{resourceId}/access-tokens",
description: "List all access tokens for a resource.", description: "List all access tokens for a resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: z.object({
resourceId: z.number()
}),
query: listAccessTokensSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/public-resource/{resourceId}/access-tokens",
description: "List all access tokens for a resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.AccessToken], tags: [OpenAPITags.PublicResource, OpenAPITags.AccessToken],
request: { request: {
params: z.object({ params: z.object({
+16
View File
@@ -248,6 +248,22 @@ authenticated.post(
site.updateSite site.updateSite
); );
authenticated.post(
"/site/:siteId/approve",
verifySiteAccess,
verifyUserHasAction(ActionsEnum.updateSiteApprovals),
logActionAudit(ActionsEnum.updateSiteApprovals),
site.approveSite
);
authenticated.post(
"/site/:siteId/reject",
verifySiteAccess,
verifyUserHasAction(ActionsEnum.updateSiteApprovals),
logActionAudit(ActionsEnum.updateSiteApprovals),
site.rejectSite
);
authenticated.delete( authenticated.delete(
"/site/:siteId", "/site/:siteId",
verifySiteAccess, verifySiteAccess,
+61 -168
View File
@@ -138,6 +138,7 @@ authenticated.post(
logActionAudit(ActionsEnum.updateSite), logActionAudit(ActionsEnum.updateSite),
site.updateSite site.updateSite
); );
authenticated.post( authenticated.post(
"/org/:orgId/reset-bandwidth", "/org/:orgId/reset-bandwidth",
verifyApiKeyOrgAccess, verifyApiKeyOrgAccess,
@@ -162,7 +163,7 @@ authenticated.get(
// Site Resource endpoints // Site Resource endpoints
authenticated.put( authenticated.put(
["/org/:orgId/site-resource", "/org/:orgId/private-resource"], "/org/:orgId/site-resource",
verifyApiKeyOrgAccess, verifyApiKeyOrgAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.createSiteResource), verifyApiKeyHasAction(ActionsEnum.createSiteResource),
@@ -171,10 +172,7 @@ authenticated.put(
); );
authenticated.get( authenticated.get(
[ "/org/:orgId/site/:siteId/resources",
"/org/:orgId/site/:siteId/resources",
"/org/:orgId/site/:siteId/private-resources"
],
verifyApiKeyOrgAccess, verifyApiKeyOrgAccess,
verifyApiKeySiteAccess, verifyApiKeySiteAccess,
verifyApiKeyHasAction(ActionsEnum.listSiteResources), verifyApiKeyHasAction(ActionsEnum.listSiteResources),
@@ -182,21 +180,21 @@ authenticated.get(
); );
authenticated.get( authenticated.get(
["/org/:orgId/site-resources", "/org/:orgId/private-resources"], "/org/:orgId/site-resources",
verifyApiKeyOrgAccess, verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.listSiteResources), verifyApiKeyHasAction(ActionsEnum.listSiteResources),
siteResource.listAllSiteResourcesByOrg siteResource.listAllSiteResourcesByOrg
); );
authenticated.get( authenticated.get(
["/site-resource/:siteResourceId", "/private-resource/:siteResourceId"], "/site-resource/:siteResourceId",
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeyHasAction(ActionsEnum.getSiteResource), verifyApiKeyHasAction(ActionsEnum.getSiteResource),
siteResource.getSiteResource siteResource.getSiteResource
); );
authenticated.post( authenticated.post(
["/site-resource/:siteResourceId", "/private-resource/:siteResourceId"], "/site-resource/:siteResourceId",
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.updateSiteResource), verifyApiKeyHasAction(ActionsEnum.updateSiteResource),
@@ -205,7 +203,7 @@ authenticated.post(
); );
authenticated.delete( authenticated.delete(
["/site-resource/:siteResourceId", "/private-resource/:siteResourceId"], "/site-resource/:siteResourceId",
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeyHasAction(ActionsEnum.deleteSiteResource), verifyApiKeyHasAction(ActionsEnum.deleteSiteResource),
logActionAudit(ActionsEnum.deleteSiteResource), logActionAudit(ActionsEnum.deleteSiteResource),
@@ -213,40 +211,28 @@ authenticated.delete(
); );
authenticated.get( authenticated.get(
[ "/site-resource/:siteResourceId/roles",
"/site-resource/:siteResourceId/roles",
"/private-resource/:siteResourceId/roles"
],
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeyHasAction(ActionsEnum.listResourceRoles), verifyApiKeyHasAction(ActionsEnum.listResourceRoles),
siteResource.listSiteResourceRoles siteResource.listSiteResourceRoles
); );
authenticated.get( authenticated.get(
[ "/site-resource/:siteResourceId/users",
"/site-resource/:siteResourceId/users",
"/private-resource/:siteResourceId/users"
],
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeyHasAction(ActionsEnum.listResourceUsers), verifyApiKeyHasAction(ActionsEnum.listResourceUsers),
siteResource.listSiteResourceUsers siteResource.listSiteResourceUsers
); );
authenticated.get( authenticated.get(
[ "/site-resource/:siteResourceId/clients",
"/site-resource/:siteResourceId/clients",
"/private-resource/:siteResourceId/clients"
],
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeyHasAction(ActionsEnum.listResourceUsers), verifyApiKeyHasAction(ActionsEnum.listResourceUsers),
siteResource.listSiteResourceClients siteResource.listSiteResourceClients
); );
authenticated.post( authenticated.post(
[ "/site-resource/:siteResourceId/roles",
"/site-resource/:siteResourceId/roles",
"/private-resource/:siteResourceId/roles"
],
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeyRoleAccess, verifyApiKeyRoleAccess,
verifyLimits, verifyLimits,
@@ -256,10 +242,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ "/site-resource/:siteResourceId/users",
"/site-resource/:siteResourceId/users",
"/private-resource/:siteResourceId/users"
],
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeySetResourceUsers, verifyApiKeySetResourceUsers,
verifyLimits, verifyLimits,
@@ -269,10 +252,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ "/site-resource/:siteResourceId/roles/add",
"/site-resource/:siteResourceId/roles/add",
"/private-resource/:siteResourceId/roles/add"
],
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeyRoleAccess, verifyApiKeyRoleAccess,
verifyLimits, verifyLimits,
@@ -282,10 +262,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ "/site-resource/:siteResourceId/roles/remove",
"/site-resource/:siteResourceId/roles/remove",
"/private-resource/:siteResourceId/roles/remove"
],
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeyRoleAccess, verifyApiKeyRoleAccess,
verifyLimits, verifyLimits,
@@ -295,10 +272,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ "/site-resource/:siteResourceId/users/add",
"/site-resource/:siteResourceId/users/add",
"/private-resource/:siteResourceId/users/add"
],
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeySetResourceUsers, verifyApiKeySetResourceUsers,
verifyLimits, verifyLimits,
@@ -308,10 +282,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ "/site-resource/:siteResourceId/users/remove",
"/site-resource/:siteResourceId/users/remove",
"/private-resource/:siteResourceId/users/remove"
],
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeySetResourceUsers, verifyApiKeySetResourceUsers,
verifyLimits, verifyLimits,
@@ -321,10 +292,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ "/site-resource/:siteResourceId/clients",
"/site-resource/:siteResourceId/clients",
"/private-resource/:siteResourceId/clients"
],
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeySetResourceClients, verifyApiKeySetResourceClients,
verifyLimits, verifyLimits,
@@ -334,10 +302,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ "/site-resource/:siteResourceId/clients/add",
"/site-resource/:siteResourceId/clients/add",
"/private-resource/:siteResourceId/clients/add"
],
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeySetResourceClients, verifyApiKeySetResourceClients,
verifyLimits, verifyLimits,
@@ -347,10 +312,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ "/site-resource/:siteResourceId/clients/remove",
"/site-resource/:siteResourceId/clients/remove",
"/private-resource/:siteResourceId/clients/remove"
],
verifyApiKeySiteResourceAccess, verifyApiKeySiteResourceAccess,
verifyApiKeySetResourceClients, verifyApiKeySetResourceClients,
verifyLimits, verifyLimits,
@@ -360,7 +322,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
["/client/:clientId/site-resources", "/client/:clientId/private-resources"], "/client/:clientId/site-resources",
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourceUsers), verifyApiKeyHasAction(ActionsEnum.setResourceUsers),
logActionAudit(ActionsEnum.setResourceUsers), logActionAudit(ActionsEnum.setResourceUsers),
@@ -368,7 +330,7 @@ authenticated.post(
); );
authenticated.put( authenticated.put(
["/org/:orgId/resource", "/org/:orgId/public-resource"], "/org/:orgId/resource",
verifyApiKeyOrgAccess, verifyApiKeyOrgAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.createResource), verifyApiKeyHasAction(ActionsEnum.createResource),
@@ -377,10 +339,7 @@ authenticated.put(
); );
authenticated.put( authenticated.put(
[ "/org/:orgId/site/:siteId/resource",
"/org/:orgId/site/:siteId/resource",
"/org/:orgId/site/:siteId/public-resource"
],
verifyApiKeyOrgAccess, verifyApiKeyOrgAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.createResource), verifyApiKeyHasAction(ActionsEnum.createResource),
@@ -389,14 +348,14 @@ authenticated.put(
); );
authenticated.get( authenticated.get(
["/site/:siteId/resources", "/site/:siteId/public-resources"], "/site/:siteId/resources",
verifyApiKeySiteAccess, verifyApiKeySiteAccess,
verifyApiKeyHasAction(ActionsEnum.listResources), verifyApiKeyHasAction(ActionsEnum.listResources),
resource.listResources resource.listResources
); );
authenticated.get( authenticated.get(
["/org/:orgId/resources", "/org/:orgId/public-resources"], "/org/:orgId/resources",
verifyApiKeyOrgAccess, verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.listResources), verifyApiKeyHasAction(ActionsEnum.listResources),
resource.listResources resource.listResources
@@ -484,45 +443,42 @@ authenticated.delete(
); );
authenticated.get( authenticated.get(
["/resource/:resourceId/roles", "/public-resource/:resourceId/roles"], "/resource/:resourceId/roles",
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.listResourceRoles), verifyApiKeyHasAction(ActionsEnum.listResourceRoles),
resource.listResourceRoles resource.listResourceRoles
); );
authenticated.get( authenticated.get(
["/resource/:resourceId/users", "/public-resource/:resourceId/users"], "/resource/:resourceId/users",
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.listResourceUsers), verifyApiKeyHasAction(ActionsEnum.listResourceUsers),
resource.listResourceUsers resource.listResourceUsers
); );
authenticated.get( authenticated.get(
["/resource/:resourceId", "/public-resource/:resourceId"], "/resource/:resourceId",
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.getResource), verifyApiKeyHasAction(ActionsEnum.getResource),
resource.getResource resource.getResource
); );
authenticated.get( authenticated.get(
[ "/resource-policy/:resourcePolicyId",
"/resource-policy/:resourcePolicyId",
"/public-resource-policy/:resourcePolicyId"
],
verifyApiKeyResourcePolicyAccess, verifyApiKeyResourcePolicyAccess,
verifyApiKeyHasAction(ActionsEnum.getResourcePolicy), verifyApiKeyHasAction(ActionsEnum.getResourcePolicy),
policy.getResourcePolicy policy.getResourcePolicy
); );
authenticated.get( authenticated.get(
["/resource/:resourceId/policies", "/public-resource/:resourceId/policies"], "/resource/:resourceId/policies",
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.getResourcePolicy), verifyApiKeyHasAction(ActionsEnum.getResourcePolicy),
resource.getResourcePolicies resource.getResourcePolicies
); );
authenticated.post( authenticated.post(
["/resource/:resourceId", "/public-resource/:resourceId"], "/resource/:resourceId",
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.updateResource), verifyApiKeyHasAction(ActionsEnum.updateResource),
@@ -531,17 +487,14 @@ authenticated.post(
); );
authenticated.put( authenticated.put(
[ "/resource-policy/:resourcePolicyId",
"/resource-policy/:resourcePolicyId",
"/public-resource-policy/:resourcePolicyId"
],
verifyApiKeyResourcePolicyAccess, verifyApiKeyResourcePolicyAccess,
verifyApiKeyHasAction(ActionsEnum.updateResourcePolicy), verifyApiKeyHasAction(ActionsEnum.updateResourcePolicy),
policy.updateResourcePolicy policy.updateResourcePolicy
); );
authenticated.delete( authenticated.delete(
["/resource/:resourceId", "/public-resource/:resourceId"], "/resource/:resourceId",
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.deleteResource), verifyApiKeyHasAction(ActionsEnum.deleteResource),
logActionAudit(ActionsEnum.deleteResource), logActionAudit(ActionsEnum.deleteResource),
@@ -549,7 +502,7 @@ authenticated.delete(
); );
authenticated.put( authenticated.put(
["/resource/:resourceId/target", "/public-resource/:resourceId/target"], "/resource/:resourceId/target",
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.createTarget), verifyApiKeyHasAction(ActionsEnum.createTarget),
@@ -558,14 +511,14 @@ authenticated.put(
); );
authenticated.get( authenticated.get(
["/resource/:resourceId/targets", "/public-resource/:resourceId/targets"], "/resource/:resourceId/targets",
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.listTargets), verifyApiKeyHasAction(ActionsEnum.listTargets),
target.listTargets target.listTargets
); );
authenticated.put( authenticated.put(
["/resource/:resourceId/rule", "/public-resource/:resourceId/rule"], "/resource/:resourceId/rule",
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.createResourceRule), verifyApiKeyHasAction(ActionsEnum.createResourceRule),
@@ -574,17 +527,14 @@ authenticated.put(
); );
authenticated.get( authenticated.get(
["/resource/:resourceId/rules", "/public-resource/:resourceId/rules"], "/resource/:resourceId/rules",
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.listResourceRules), verifyApiKeyHasAction(ActionsEnum.listResourceRules),
resource.listResourceRules resource.listResourceRules
); );
authenticated.post( authenticated.post(
[ "/resource/:resourceId/rule/:ruleId",
"/resource/:resourceId/rule/:ruleId",
"/public-resource/:resourceId/rule/:ruleId"
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.updateResourceRule), verifyApiKeyHasAction(ActionsEnum.updateResourceRule),
@@ -593,10 +543,7 @@ authenticated.post(
); );
authenticated.delete( authenticated.delete(
[ "/resource/:resourceId/rule/:ruleId",
"/resource/:resourceId/rule/:ruleId",
"/public-resource/:resourceId/rule/:ruleId"
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.deleteResourceRule), verifyApiKeyHasAction(ActionsEnum.deleteResourceRule),
logActionAudit(ActionsEnum.deleteResourceRule), logActionAudit(ActionsEnum.deleteResourceRule),
@@ -678,7 +625,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
["/resource/:resourceId/roles", "/public-resource/:resourceId/roles"], "/resource/:resourceId/roles",
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyRoleAccess, verifyApiKeyRoleAccess,
verifyLimits, verifyLimits,
@@ -688,7 +635,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
["/resource/:resourceId/users", "/public-resource/:resourceId/users"], "/resource/:resourceId/users",
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeySetResourceUsers, verifyApiKeySetResourceUsers,
verifyLimits, verifyLimits,
@@ -698,10 +645,7 @@ authenticated.post(
); );
authenticated.put( authenticated.put(
[ "/resource-policy/:resourcePolicyId/access-control",
"/resource-policy/:resourcePolicyId/access-control",
"/public-resource-policy/:resourcePolicyId/access-control"
],
verifyApiKeyResourcePolicyAccess, verifyApiKeyResourcePolicyAccess,
verifyApiKeyRoleAccess, verifyApiKeyRoleAccess,
verifyLimits, verifyLimits,
@@ -713,10 +657,7 @@ authenticated.put(
); );
authenticated.put( authenticated.put(
[ "/resource-policy/:resourcePolicyId/password",
"/resource-policy/:resourcePolicyId/password",
"/public-resource-policy/:resourcePolicyId/password"
],
verifyApiKeyResourcePolicyAccess, verifyApiKeyResourcePolicyAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyPassword), verifyApiKeyHasAction(ActionsEnum.setResourcePolicyPassword),
@@ -725,10 +666,7 @@ authenticated.put(
); );
authenticated.put( authenticated.put(
[ "/resource-policy/:resourcePolicyId/pincode",
"/resource-policy/:resourcePolicyId/pincode",
"/public-resource-policy/:resourcePolicyId/pincode"
],
verifyApiKeyResourcePolicyAccess, verifyApiKeyResourcePolicyAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyPincode), verifyApiKeyHasAction(ActionsEnum.setResourcePolicyPincode),
@@ -737,10 +675,7 @@ authenticated.put(
); );
authenticated.put( authenticated.put(
[ "/resource-policy/:resourcePolicyId/header-auth",
"/resource-policy/:resourcePolicyId/header-auth",
"/public-resource-policy/:resourcePolicyId/header-auth"
],
verifyApiKeyResourcePolicyAccess, verifyApiKeyResourcePolicyAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyHeaderAuth), verifyApiKeyHasAction(ActionsEnum.setResourcePolicyHeaderAuth),
@@ -749,10 +684,7 @@ authenticated.put(
); );
authenticated.put( authenticated.put(
[ "/resource-policy/:resourcePolicyId/whitelist",
"/resource-policy/:resourcePolicyId/whitelist",
"/public-resource-policy/:resourcePolicyId/whitelist"
],
verifyApiKeyResourcePolicyAccess, verifyApiKeyResourcePolicyAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyWhitelist), verifyApiKeyHasAction(ActionsEnum.setResourcePolicyWhitelist),
@@ -761,10 +693,7 @@ authenticated.put(
); );
authenticated.put( authenticated.put(
[ "/resource-policy/:resourcePolicyId/rules",
"/resource-policy/:resourcePolicyId/rules",
"/public-resource-policy/:resourcePolicyId/rules"
],
verifyApiKeyResourcePolicyAccess, verifyApiKeyResourcePolicyAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyRules), verifyApiKeyHasAction(ActionsEnum.setResourcePolicyRules),
@@ -773,10 +702,7 @@ authenticated.put(
); );
authenticated.post( authenticated.post(
[ "/resource/:resourceId/roles/add",
"/resource/:resourceId/roles/add",
"/public-resource/:resourceId/roles/add"
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyRoleAccess, verifyApiKeyRoleAccess,
verifyLimits, verifyLimits,
@@ -786,10 +712,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ "/resource/:resourceId/roles/remove",
"/resource/:resourceId/roles/remove",
"/public-resource/:resourceId/roles/remove"
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyRoleAccess, verifyApiKeyRoleAccess,
verifyLimits, verifyLimits,
@@ -799,10 +722,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ "/resource/:resourceId/users/add",
"/resource/:resourceId/users/add",
"/public-resource/:resourceId/users/add"
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeySetResourceUsers, verifyApiKeySetResourceUsers,
verifyLimits, verifyLimits,
@@ -812,10 +732,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ "/resource/:resourceId/users/remove",
"/resource/:resourceId/users/remove",
"/public-resource/:resourceId/users/remove"
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeySetResourceUsers, verifyApiKeySetResourceUsers,
verifyLimits, verifyLimits,
@@ -825,7 +742,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[`/resource/:resourceId/password`, `/public-resource/:resourceId/password`], `/resource/:resourceId/password`,
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourcePassword), verifyApiKeyHasAction(ActionsEnum.setResourcePassword),
@@ -834,7 +751,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[`/resource/:resourceId/pincode`, `/public-resource/:resourceId/pincode`], `/resource/:resourceId/pincode`,
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourcePincode), verifyApiKeyHasAction(ActionsEnum.setResourcePincode),
@@ -843,10 +760,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ `/resource/:resourceId/header-auth`,
`/resource/:resourceId/header-auth`,
`/public-resource/:resourceId/header-auth`
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourceHeaderAuth), verifyApiKeyHasAction(ActionsEnum.setResourceHeaderAuth),
@@ -855,10 +769,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ `/resource/:resourceId/whitelist`,
`/resource/:resourceId/whitelist`,
`/public-resource/:resourceId/whitelist`
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist), verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist),
@@ -867,10 +778,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ `/resource/:resourceId/whitelist/add`,
`/resource/:resourceId/whitelist/add`,
`/public-resource/:resourceId/whitelist/add`
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist), verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist),
@@ -878,10 +786,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ `/resource/:resourceId/whitelist/remove`,
`/resource/:resourceId/whitelist/remove`,
`/public-resource/:resourceId/whitelist/remove`
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist), verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist),
@@ -889,20 +794,14 @@ authenticated.post(
); );
authenticated.get( authenticated.get(
[ `/resource/:resourceId/whitelist`,
`/resource/:resourceId/whitelist`,
`/public-resource/:resourceId/whitelist`
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.getResourceWhitelist), verifyApiKeyHasAction(ActionsEnum.getResourceWhitelist),
resource.getResourceWhitelist resource.getResourceWhitelist
); );
authenticated.post( authenticated.post(
[ `/resource/:resourceId/access-token`,
`/resource/:resourceId/access-token`,
`/public-resource/:resourceId/access-token`
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyLimits, verifyLimits,
verifyApiKeyHasAction(ActionsEnum.generateAccessToken), verifyApiKeyHasAction(ActionsEnum.generateAccessToken),
@@ -911,10 +810,7 @@ authenticated.post(
); );
authenticated.post( authenticated.post(
[ `/resource/:resourceId/session-token`,
`/resource/:resourceId/session-token`,
`/public-resource/:resourceId/session-token`
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyUserAccess, verifyApiKeyUserAccess,
verifyLimits, verifyLimits,
@@ -939,10 +835,7 @@ authenticated.get(
); );
authenticated.get( authenticated.get(
[ `/resource/:resourceId/access-tokens`,
`/resource/:resourceId/access-tokens`,
`/public-resource/:resourceId/access-tokens`
],
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.listAccessTokens), verifyApiKeyHasAction(ActionsEnum.listAccessTokens),
accessToken.listAccessTokens accessToken.listAccessTokens
@@ -1272,7 +1165,7 @@ authenticated.get(
); );
authenticated.get( authenticated.get(
["/org/:orgId/resource-names", "/org/:orgId/public-resource-names"], "/org/:orgId/resource-names",
verifyApiKeyOrgAccess, verifyApiKeyOrgAccess,
verifyApiKeyHasAction(ActionsEnum.listResources), verifyApiKeyHasAction(ActionsEnum.listResources),
resource.listAllResourceNames resource.listAllResourceNames
@@ -157,7 +157,8 @@ async function resolveAccessibleIdsUncached(
.where( .where(
and( and(
eq(userResources.userId, userId), eq(userResources.userId, userId),
eq(resources.orgId, orgId) eq(resources.orgId, orgId),
eq(resources.status, "approved")
) )
), ),
userRoleIds.length > 0 userRoleIds.length > 0
@@ -171,7 +172,8 @@ async function resolveAccessibleIdsUncached(
.where( .where(
and( and(
inArray(roleResources.roleId, userRoleIds), inArray(roleResources.roleId, userRoleIds),
eq(resources.orgId, orgId) eq(resources.orgId, orgId),
eq(resources.status, "approved")
) )
) )
: Promise.resolve([]), : Promise.resolve([]),
@@ -183,7 +185,11 @@ async function resolveAccessibleIdsUncached(
eq(effectiveResourcePolicyId, userPolicies.resourcePolicyId) eq(effectiveResourcePolicyId, userPolicies.resourcePolicyId)
) )
.where( .where(
and(eq(userPolicies.userId, userId), eq(resources.orgId, orgId)) and(
eq(userPolicies.userId, userId),
eq(resources.orgId, orgId),
eq(resources.status, "approved")
)
), ),
userRoleIds.length > 0 userRoleIds.length > 0
? db ? db
@@ -199,21 +205,48 @@ async function resolveAccessibleIdsUncached(
.where( .where(
and( and(
inArray(rolePolicies.roleId, userRoleIds), inArray(rolePolicies.roleId, userRoleIds),
eq(resources.orgId, orgId) eq(resources.orgId, orgId),
eq(resources.status, "approved")
) )
) )
: Promise.resolve([]), : Promise.resolve([]),
db db
.select({ siteResourceId: userSiteResources.siteResourceId }) .select({ siteResourceId: userSiteResources.siteResourceId })
.from(userSiteResources) .from(userSiteResources)
.where(eq(userSiteResources.userId, userId)), .innerJoin(
siteResources,
eq(
userSiteResources.siteResourceId,
siteResources.siteResourceId
)
)
.where(
and(
eq(userSiteResources.userId, userId),
eq(siteResources.orgId, orgId),
eq(siteResources.status, "approved")
)
),
userRoleIds.length > 0 userRoleIds.length > 0
? db ? db
.select({ .select({
siteResourceId: roleSiteResources.siteResourceId siteResourceId: roleSiteResources.siteResourceId
}) })
.from(roleSiteResources) .from(roleSiteResources)
.where(inArray(roleSiteResources.roleId, userRoleIds)) .innerJoin(
siteResources,
eq(
roleSiteResources.siteResourceId,
siteResources.siteResourceId
)
)
.where(
and(
inArray(roleSiteResources.roleId, userRoleIds),
eq(siteResources.orgId, orgId),
eq(siteResources.status, "approved")
)
)
: Promise.resolve([]) : Promise.resolve([])
]); ]);
@@ -365,6 +398,7 @@ async function filterPublicResourceIdsByTextSearch(
inArray(resources.resourceId, resourceIds), inArray(resources.resourceId, resourceIds),
eq(resources.orgId, orgId), eq(resources.orgId, orgId),
eq(resources.enabled, true), eq(resources.enabled, true),
eq(resources.status, "approved"),
textMatch textMatch
) )
); );
@@ -402,6 +436,7 @@ async function filterSiteResourceIdsByTextSearch(
inArray(siteResources.siteResourceId, siteResourceIds), inArray(siteResources.siteResourceId, siteResourceIds),
eq(siteResources.orgId, orgId), eq(siteResources.orgId, orgId),
eq(siteResources.enabled, true), eq(siteResources.enabled, true),
eq(siteResources.status, "approved"),
textMatch textMatch
) )
); );
@@ -503,7 +538,8 @@ async function listSiteGroups(
const publicConditions = [ const publicConditions = [
inArray(resources.resourceId, accessible.resourceIds), inArray(resources.resourceId, accessible.resourceIds),
eq(resources.orgId, orgId), eq(resources.orgId, orgId),
eq(resources.enabled, true) eq(resources.enabled, true),
eq(resources.status, "approved")
]; ];
if (searchPublic) { if (searchPublic) {
publicConditions.push(searchPublic); publicConditions.push(searchPublic);
@@ -558,7 +594,8 @@ async function listSiteGroups(
const siteConditions = [ const siteConditions = [
inArray(siteResources.siteResourceId, accessible.siteResourceIds), inArray(siteResources.siteResourceId, accessible.siteResourceIds),
eq(siteResources.orgId, orgId), eq(siteResources.orgId, orgId),
eq(siteResources.enabled, true) eq(siteResources.enabled, true),
eq(siteResources.status, "approved")
]; ];
if (searchSite) { if (searchSite) {
siteConditions.push(searchSite); siteConditions.push(searchSite);
@@ -621,7 +658,8 @@ async function listSiteGroups(
const noSitePublicConditions = [ const noSitePublicConditions = [
inArray(resources.resourceId, accessible.resourceIds), inArray(resources.resourceId, accessible.resourceIds),
eq(resources.orgId, orgId), eq(resources.orgId, orgId),
eq(resources.enabled, true) eq(resources.enabled, true),
eq(resources.status, "approved")
]; ];
if (searchPublic) { if (searchPublic) {
noSitePublicConditions.push(searchPublic); noSitePublicConditions.push(searchPublic);
@@ -655,7 +693,8 @@ async function listSiteGroups(
const noSiteSiteConditions = [ const noSiteSiteConditions = [
inArray(siteResources.siteResourceId, accessible.siteResourceIds), inArray(siteResources.siteResourceId, accessible.siteResourceIds),
eq(siteResources.orgId, orgId), eq(siteResources.orgId, orgId),
eq(siteResources.enabled, true) eq(siteResources.enabled, true),
eq(siteResources.status, "approved")
]; ];
if (searchSite) { if (searchSite) {
noSiteSiteConditions.push(searchSite); noSiteSiteConditions.push(searchSite);
@@ -746,7 +785,8 @@ async function listLabelGroups(
const publicConditions = [ const publicConditions = [
inArray(resources.resourceId, accessible.resourceIds), inArray(resources.resourceId, accessible.resourceIds),
eq(resources.orgId, orgId), eq(resources.orgId, orgId),
eq(resources.enabled, true) eq(resources.enabled, true),
eq(resources.status, "approved")
]; ];
const searchPublic = buildSearchConditionForPublic(query.query); const searchPublic = buildSearchConditionForPublic(query.query);
if (searchPublic) { if (searchPublic) {
@@ -810,7 +850,8 @@ async function listLabelGroups(
const siteConditions = [ const siteConditions = [
inArray(siteResources.siteResourceId, accessible.siteResourceIds), inArray(siteResources.siteResourceId, accessible.siteResourceIds),
eq(siteResources.orgId, orgId), eq(siteResources.orgId, orgId),
eq(siteResources.enabled, true) eq(siteResources.enabled, true),
eq(siteResources.status, "approved")
]; ];
const searchSite = buildSearchConditionForSiteResource(query.query); const searchSite = buildSearchConditionForSiteResource(query.query);
if (searchSite) { if (searchSite) {
@@ -997,6 +1038,7 @@ async function mapPublicResources(
inArray(resources.resourceId, resourceIds), inArray(resources.resourceId, resourceIds),
eq(resources.orgId, orgId), eq(resources.orgId, orgId),
eq(resources.enabled, true), eq(resources.enabled, true),
eq(resources.status, "approved"),
siteIdFilter != null siteIdFilter != null
? eq(sites.siteId, siteIdFilter) ? eq(sites.siteId, siteIdFilter)
: undefined : undefined
@@ -1088,6 +1130,7 @@ async function mapSiteResources(
inArray(siteResources.siteResourceId, siteResourceIds), inArray(siteResources.siteResourceId, siteResourceIds),
eq(siteResources.orgId, orgId), eq(siteResources.orgId, orgId),
eq(siteResources.enabled, true), eq(siteResources.enabled, true),
eq(siteResources.status, "approved"),
siteIdFilter != null siteIdFilter != null
? eq(sites.siteId, siteIdFilter) ? eq(sites.siteId, siteIdFilter)
: undefined : undefined
@@ -1382,7 +1425,8 @@ async function collectAccessibleSites(
const publicConditions = [ const publicConditions = [
inArray(resources.resourceId, accessible.resourceIds), inArray(resources.resourceId, accessible.resourceIds),
eq(resources.orgId, orgId), eq(resources.orgId, orgId),
eq(resources.enabled, true) eq(resources.enabled, true),
eq(resources.status, "approved")
]; ];
if (siteNameSearch) { if (siteNameSearch) {
publicConditions.push(siteNameSearch); publicConditions.push(siteNameSearch);
@@ -1422,7 +1466,8 @@ async function collectAccessibleSites(
const siteConditions = [ const siteConditions = [
inArray(siteResources.siteResourceId, accessible.siteResourceIds), inArray(siteResources.siteResourceId, accessible.siteResourceIds),
eq(siteResources.orgId, orgId), eq(siteResources.orgId, orgId),
eq(siteResources.enabled, true) eq(siteResources.enabled, true),
eq(siteResources.status, "approved")
]; ];
if (siteNameSearch) { if (siteNameSearch) {
siteConditions.push(siteNameSearch); siteConditions.push(siteNameSearch);
@@ -1476,6 +1521,7 @@ async function collectAccessibleLabels(
inArray(resources.resourceId, accessible.resourceIds), inArray(resources.resourceId, accessible.resourceIds),
eq(resources.orgId, orgId), eq(resources.orgId, orgId),
eq(resources.enabled, true), eq(resources.enabled, true),
eq(resources.status, "approved"),
eq(labels.orgId, orgId) eq(labels.orgId, orgId)
]; ];
if (labelNameSearch) { if (labelNameSearch) {
@@ -1511,6 +1557,7 @@ async function collectAccessibleLabels(
inArray(siteResources.siteResourceId, accessible.siteResourceIds), inArray(siteResources.siteResourceId, accessible.siteResourceIds),
eq(siteResources.orgId, orgId), eq(siteResources.orgId, orgId),
eq(siteResources.enabled, true), eq(siteResources.enabled, true),
eq(siteResources.status, "approved"),
eq(labels.orgId, orgId) eq(labels.orgId, orgId)
]; ];
if (labelNameSearch) { if (labelNameSearch) {
+6 -1
View File
@@ -148,7 +148,12 @@ export async function buildClientConfigurationForNewtClient(
.from(siteResources) .from(siteResources)
.innerJoin(networks, eq(siteResources.networkId, networks.networkId)) .innerJoin(networks, eq(siteResources.networkId, networks.networkId))
.innerJoin(siteNetworks, eq(networks.networkId, siteNetworks.networkId)) .innerJoin(siteNetworks, eq(networks.networkId, siteNetworks.networkId))
.where(eq(siteNetworks.siteId, siteId)) .where(
and(
eq(siteNetworks.siteId, siteId),
eq(siteResources.enabled, true)
)
)
.then((rows) => rows.map((r) => r.siteResources)); .then((rows) => rows.map((r) => r.siteResources));
const targetsToSend: SubnetProxyTargetV2[] = []; const targetsToSend: SubnetProxyTargetV2[] = [];
+8 -2
View File
@@ -16,7 +16,7 @@ import {
generateRemoteSubnets generateRemoteSubnets
} from "@server/lib/ip"; } from "@server/lib/ip";
import logger from "@server/logger"; import logger from "@server/logger";
import { eq, inArray } from "drizzle-orm"; import { and, eq, inArray } from "drizzle-orm";
import { addPeer, deletePeer } from "../newt/peers"; import { addPeer, deletePeer } from "../newt/peers";
import config from "@server/lib/config"; import config from "@server/lib/config";
@@ -70,7 +70,13 @@ export async function buildSiteConfigurationForOlmClient(
.innerJoin(networks, eq(siteResources.networkId, networks.networkId)) .innerJoin(networks, eq(siteResources.networkId, networks.networkId))
.innerJoin(siteNetworks, eq(networks.networkId, siteNetworks.networkId)) .innerJoin(siteNetworks, eq(networks.networkId, siteNetworks.networkId))
.where( .where(
eq(clientSiteResourcesAssociationsCache.clientId, client.clientId) and(
eq(
clientSiteResourcesAssociationsCache.clientId,
client.clientId
),
eq(siteResources.enabled, true)
)
); );
const siteResourcesBySiteId = new Map<number, SiteResource[]>(); const siteResourcesBySiteId = new Map<number, SiteResource[]>();
+2 -15
View File
@@ -168,7 +168,7 @@ registry.registerPath({
path: "/org/{orgId}/resource-policy/{niceId}", path: "/org/{orgId}/resource-policy/{niceId}",
description: description:
"Get a resource policy by orgId and niceId. NiceId is a readable ID for the resource and unique on a per org basis.", "Get a resource policy by orgId and niceId. NiceId is a readable ID for the resource and unique on a per org basis.",
tags: [OpenAPITags.PublicResourcePolicy], tags: [OpenAPITags.Org, OpenAPITags.Policy],
request: { request: {
params: z.object({ params: z.object({
orgId: z.string(), orgId: z.string(),
@@ -182,20 +182,7 @@ registry.registerPath({
method: "get", method: "get",
path: "/resource-policy/{resourcePolicyId}", path: "/resource-policy/{resourcePolicyId}",
description: "Get a resource policy by its resourcePolicyId.", description: "Get a resource policy by its resourcePolicyId.",
tags: [OpenAPITags.PublicResourcePolicyLegacy], tags: [OpenAPITags.Policy],
request: {
params: z.object({
resourcePolicyId: z.number()
})
},
responses: {}
});
registry.registerPath({
method: "get",
path: "/public-resource-policy/{resourcePolicyId}",
description: "Get a resource policy by its resourcePolicyId.",
tags: [OpenAPITags.PublicResourcePolicy],
request: { request: {
params: z.object({ params: z.object({
resourcePolicyId: z.number() resourcePolicyId: z.number()
@@ -40,26 +40,7 @@ registry.registerPath({
path: "/resource-policy/{resourceId}/access-control", path: "/resource-policy/{resourceId}/access-control",
description: description:
"Set access control users for a resource policy, including SSO, users, roles, Identity provider.", "Set access control users for a resource policy, including SSO, users, roles, Identity provider.",
tags: [OpenAPITags.PublicResourcePolicyLegacy], tags: [OpenAPITags.Policy, OpenAPITags.User],
request: {
params: setResourcePolicyAccessControlParamsSchema,
body: {
content: {
"application/json": {
schema: setResourcePolicyAcccessControlBodySchema
}
}
}
},
responses: {}
});
registry.registerPath({
method: "post",
path: "/public-resource-policy/{resourceId}/access-control",
description:
"Set access control users for a resource policy, including SSO, users, roles, Identity provider.",
tags: [OpenAPITags.PublicResourcePolicy, OpenAPITags.User],
request: { request: {
params: setResourcePolicyAccessControlParamsSchema, params: setResourcePolicyAccessControlParamsSchema,
body: { body: {
@@ -29,26 +29,7 @@ registry.registerPath({
path: "/resource-policy/{resourcePolicyId}/header-auth", path: "/resource-policy/{resourcePolicyId}/header-auth",
description: description:
"Set or update the header authentication for a resource policy. If user and password is not provided, it will remove the header authentication.", "Set or update the header authentication for a resource policy. If user and password is not provided, it will remove the header authentication.",
tags: [OpenAPITags.PublicResourcePolicyLegacy], tags: [OpenAPITags.Policy],
request: {
params: setResourcePolicyHeaderAuthParamsSchema,
body: {
content: {
"application/json": {
schema: setResourcePolicyHeaderAuthBodySchema
}
}
}
},
responses: {}
});
registry.registerPath({
method: "put",
path: "/public-resource-policy/{resourcePolicyId}/header-auth",
description:
"Set or update the header authentication for a resource policy. If user and password is not provided, it will remove the header authentication.",
tags: [OpenAPITags.PublicResourcePolicy],
request: { request: {
params: setResourcePolicyHeaderAuthParamsSchema, params: setResourcePolicyHeaderAuthParamsSchema,
body: { body: {
@@ -24,26 +24,7 @@ registry.registerPath({
path: "/resource-policy/{resourcePolicyId}/password", path: "/resource-policy/{resourcePolicyId}/password",
description: description:
"Set the password for a resource policy. Setting the password to null will remove it.", "Set the password for a resource policy. Setting the password to null will remove it.",
tags: [OpenAPITags.PublicResourcePolicyLegacy], tags: [OpenAPITags.Policy],
request: {
params: setResourcePolicyPasswordParamsSchema,
body: {
content: {
"application/json": {
schema: setResourcePolicyPasswordBodySchema
}
}
}
},
responses: {}
});
registry.registerPath({
method: "put",
path: "/public-resource-policy/{resourcePolicyId}/password",
description:
"Set the password for a resource policy. Setting the password to null will remove it.",
tags: [OpenAPITags.PublicResourcePolicy],
request: { request: {
params: setResourcePolicyPasswordParamsSchema, params: setResourcePolicyPasswordParamsSchema,
body: { body: {
@@ -27,26 +27,7 @@ registry.registerPath({
path: "/resource-policy/{resourcePolicyId}/pincode", path: "/resource-policy/{resourcePolicyId}/pincode",
description: description:
"Set the PIN code for a resource policy. Setting the PIN code to null will remove it.", "Set the PIN code for a resource policy. Setting the PIN code to null will remove it.",
tags: [OpenAPITags.PublicResourcePolicyLegacy], tags: [OpenAPITags.Policy],
request: {
params: setResourcePolicyPincodeParamsSchema,
body: {
content: {
"application/json": {
schema: setResourcePolicyPincodeBodySchema
}
}
}
},
responses: {}
});
registry.registerPath({
method: "put",
path: "/public-resource-policy/{resourcePolicyId}/pincode",
description:
"Set the PIN code for a resource policy. Setting the PIN code to null will remove it.",
tags: [OpenAPITags.PublicResourcePolicy],
request: { request: {
params: setResourcePolicyPincodeParamsSchema, params: setResourcePolicyPincodeParamsSchema,
body: { body: {
@@ -47,26 +47,7 @@ registry.registerPath({
path: "/resource-policy/{resourcePolicyId}/rules", path: "/resource-policy/{resourcePolicyId}/rules",
description: description:
"Set all rules for a resource policy at once. This will replace all existing rules.", "Set all rules for a resource policy at once. This will replace all existing rules.",
tags: [OpenAPITags.PublicResourcePolicyLegacy], tags: [OpenAPITags.Policy],
request: {
params: setResourcePolicyRulesParamsSchema,
body: {
content: {
"application/json": {
schema: setResourcePolicyRulesBodySchema
}
}
}
},
responses: {}
});
registry.registerPath({
method: "put",
path: "/public-resource-policy/{resourcePolicyId}/rules",
description:
"Set all rules for a resource policy at once. This will replace all existing rules.",
tags: [OpenAPITags.PublicResourcePolicy],
request: { request: {
params: setResourcePolicyRulesParamsSchema, params: setResourcePolicyRulesParamsSchema,
body: { body: {
@@ -32,26 +32,7 @@ registry.registerPath({
path: "/resource-policy/{resourcePolicyId}/whitelist", path: "/resource-policy/{resourcePolicyId}/whitelist",
description: description:
"Set email whitelist for a resource policy. This will replace all existing emails.", "Set email whitelist for a resource policy. This will replace all existing emails.",
tags: [OpenAPITags.PublicResourcePolicyLegacy], tags: [OpenAPITags.Policy],
request: {
params: setResourcePolicyWhitelistParamsSchema,
body: {
content: {
"application/json": {
schema: setResourcePolicyWhitelistBodySchema
}
}
}
},
responses: {}
});
registry.registerPath({
method: "put",
path: "/public-resource-policy/{resourcePolicyId}/whitelist",
description:
"Set email whitelist for a resource policy. This will replace all existing emails.",
tags: [OpenAPITags.PublicResourcePolicy],
request: { request: {
params: setResourcePolicyWhitelistParamsSchema, params: setResourcePolicyWhitelistParamsSchema,
body: { body: {
+1 -19
View File
@@ -22,25 +22,7 @@ registry.registerPath({
method: "put", method: "put",
path: "/resource-policy/{resourcePolicyId}", path: "/resource-policy/{resourcePolicyId}",
description: "Update a resource policy.", description: "Update a resource policy.",
tags: [OpenAPITags.PublicResourcePolicy], tags: [OpenAPITags.Org, OpenAPITags.Policy],
request: {
params: updateResourcePolicyParamsSchema,
body: {
content: {
"application/json": {
schema: updateResourcePolicyBodySchema
}
}
}
},
responses: {}
});
registry.registerPath({
method: "put",
path: "/public-resource-policy/{resourcePolicyId}",
description: "Update a resource policy.",
tags: [OpenAPITags.PublicResourcePolicy],
request: { request: {
params: updateResourcePolicyParamsSchema, params: updateResourcePolicyParamsSchema,
body: { body: {
@@ -34,39 +34,6 @@ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/whitelist/add", path: "/resource/{resourceId}/whitelist/add",
description: "Add a single email to the resource whitelist.", description: "Add a single email to the resource whitelist.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: addEmailToResourceWhitelistParamsSchema,
body: {
content: {
"application/json": {
schema: addEmailToResourceWhitelistBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/whitelist/add",
description: "Add a single email to the resource whitelist.",
tags: [OpenAPITags.PublicResource], tags: [OpenAPITags.PublicResource],
request: { request: {
params: addEmailToResourceWhitelistParamsSchema, params: addEmailToResourceWhitelistParamsSchema,
@@ -177,7 +144,10 @@ export async function addEmailToResourceWhitelist(
.from(resourcePolicyWhiteList) .from(resourcePolicyWhiteList)
.where( .where(
and( and(
eq(resourcePolicyWhiteList.resourcePolicyId, policyId), eq(
resourcePolicyWhiteList.resourcePolicyId,
policyId
),
eq(resourcePolicyWhiteList.email, email) eq(resourcePolicyWhiteList.email, email)
) )
); );
@@ -28,40 +28,6 @@ const addRoleToResourceParamsSchema = z
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/roles/add", path: "/resource/{resourceId}/roles/add",
description:
"Add a single role to a resource. When the resource has an inline policy defined (no shared resource policy assigned), the role is added to the inline policy instead of directly to the resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: addRoleToResourceParamsSchema,
body: {
content: {
"application/json": {
schema: addRoleToResourceBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/roles/add",
description: description:
"Add a single role to a resource. When the resource has an inline policy defined (no shared resource policy assigned), the role is added to the inline policy instead of directly to the resource.", "Add a single role to a resource. When the resource has an inline policy defined (no shared resource policy assigned), the role is added to the inline policy instead of directly to the resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.Role], tags: [OpenAPITags.PublicResource, OpenAPITags.Role],
@@ -28,40 +28,6 @@ const addUserToResourceParamsSchema = z
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/users/add", path: "/resource/{resourceId}/users/add",
description:
"Add a single user to a resource. When the resource has an inline policy defined (no shared resource policy assigned), the user is added to the inline policy instead of directly to the resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: addUserToResourceParamsSchema,
body: {
content: {
"application/json": {
schema: addUserToResourceBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/users/add",
description: description:
"Add a single user to a resource. When the resource has an inline policy defined (no shared resource policy assigned), the user is added to the inline policy instead of directly to the resource.", "Add a single user to a resource. When the resource has an inline policy defined (no shared resource policy assigned), the user is added to the inline policy instead of directly to the resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.User], tags: [OpenAPITags.PublicResource, OpenAPITags.User],
-33
View File
@@ -153,39 +153,6 @@ registry.registerPath({
method: "put", method: "put",
path: "/org/{orgId}/resource", path: "/org/{orgId}/resource",
description: "Create a resource.", description: "Create a resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: createResourceParamsSchema,
body: {
content: {
"application/json": {
schema: createHttpResourceSchema.or(createRawResourceSchema)
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "put",
path: "/org/{orgId}/public-resource",
description: "Create a resource.",
tags: [OpenAPITags.PublicResource], tags: [OpenAPITags.PublicResource],
request: { request: {
params: createResourceParamsSchema, params: createResourceParamsSchema,
@@ -32,39 +32,6 @@ registry.registerPath({
method: "put", method: "put",
path: "/resource/{resourceId}/rule", path: "/resource/{resourceId}/rule",
description: "Create a resource rule.", description: "Create a resource rule.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: createResourceRuleParamsSchema,
body: {
content: {
"application/json": {
schema: createResourceRuleSchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "put",
path: "/public-resource/{resourceId}/rule",
description: "Create a resource rule.",
tags: [OpenAPITags.PublicResource, OpenAPITags.Rule], tags: [OpenAPITags.PublicResource, OpenAPITags.Rule],
request: { request: {
params: createResourceRuleParamsSchema, params: createResourceRuleParamsSchema,
-26
View File
@@ -22,32 +22,6 @@ registry.registerPath({
method: "delete", method: "delete",
path: "/resource/{resourceId}", path: "/resource/{resourceId}",
description: "Delete a resource.", description: "Delete a resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: deleteResourceSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "delete",
path: "/public-resource/{resourceId}",
description: "Delete a resource.",
tags: [OpenAPITags.PublicResource], tags: [OpenAPITags.PublicResource],
request: { request: {
params: deleteResourceSchema params: deleteResourceSchema
@@ -19,32 +19,6 @@ registry.registerPath({
method: "delete", method: "delete",
path: "/resource/{resourceId}/rule/{ruleId}", path: "/resource/{resourceId}/rule/{ruleId}",
description: "Delete a resource rule.", description: "Delete a resource rule.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: deleteResourceRuleSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "delete",
path: "/public-resource/{resourceId}/rule/{ruleId}",
description: "Delete a resource rule.",
tags: [OpenAPITags.PublicResource, OpenAPITags.Rule], tags: [OpenAPITags.PublicResource, OpenAPITags.Rule],
request: { request: {
params: deleteResourceRuleSchema params: deleteResourceRuleSchema
+1 -29
View File
@@ -63,7 +63,7 @@ registry.registerPath({
path: "/org/{orgId}/resource/{niceId}", path: "/org/{orgId}/resource/{niceId}",
description: description:
"Get a resource by orgId and niceId. NiceId is a readable ID for the resource and unique on a per org basis.", "Get a resource by orgId and niceId. NiceId is a readable ID for the resource and unique on a per org basis.",
tags: [OpenAPITags.PublicResourceLegacy], tags: [OpenAPITags.PublicResource],
request: { request: {
params: z.object({ params: z.object({
orgId: z.string(), orgId: z.string(),
@@ -92,34 +92,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/resource/{resourceId}", path: "/resource/{resourceId}",
description: "Get a resource by resourceId.", description: "Get a resource by resourceId.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: z.object({
resourceId: z.number()
})
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/public-resource/{resourceId}",
description: "Get a resource by resourceId.",
tags: [OpenAPITags.PublicResource], tags: [OpenAPITags.PublicResource],
request: { request: {
params: z.object({ params: z.object({
+2 -15
View File
@@ -25,21 +25,8 @@ export type GetResourcePoliciesResponse = {
registry.registerPath({ registry.registerPath({
method: "get", method: "get",
path: "/resource/{resourceId}/policies", path: "/resource/{resourceId}/policies",
description: description: "Get the inline and shared policies associated with a resource.",
"Get the inline and shared policies associated with a resource.", tags: [OpenAPITags.PublicResource, OpenAPITags.Policy],
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: getResourcePoliciesParamsSchema
},
responses: {}
});
registry.registerPath({
method: "get",
path: "/public-resource/{resourceId}/policies",
description:
"Get the inline and shared policies associated with a resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.PublicResourcePolicy],
request: { request: {
params: getResourcePoliciesParamsSchema params: getResourcePoliciesParamsSchema
}, },
@@ -44,32 +44,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/resource/{resourceId}/whitelist", path: "/resource/{resourceId}/whitelist",
description: "Get the whitelist of emails for a specific resource.", description: "Get the whitelist of emails for a specific resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: getResourceWhitelistSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/public-resource/{resourceId}/whitelist",
description: "Get the whitelist of emails for a specific resource.",
tags: [OpenAPITags.PublicResource], tags: [OpenAPITags.PublicResource],
request: { request: {
params: getResourceWhitelistSchema params: getResourceWhitelistSchema
@@ -33,34 +33,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/org/{orgId}/resources-names", path: "/org/{orgId}/resources-names",
description: "List all resource names for an organization.", description: "List all resource names for an organization.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: z.object({
orgId: z.string()
})
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/org/{orgId}/public-resource-names",
description: "List all resource names for an organization.",
tags: [OpenAPITags.PublicResource], tags: [OpenAPITags.PublicResource],
request: { request: {
params: z.object({ params: z.object({
@@ -48,32 +48,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/resource/{resourceId}/roles", path: "/resource/{resourceId}/roles",
description: "List all roles for a resource.", description: "List all roles for a resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: listResourceRolesSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/public-resource/{resourceId}/roles",
description: "List all roles for a resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.Role], tags: [OpenAPITags.PublicResource, OpenAPITags.Role],
request: { request: {
params: listResourceRolesSchema params: listResourceRolesSchema
@@ -71,33 +71,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/resource/{resourceId}/rules", path: "/resource/{resourceId}/rules",
description: "List rules for a resource.", description: "List rules for a resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: listResourceRulesParamsSchema,
query: listResourceRulesSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/public-resource/{resourceId}/rules",
description: "List rules for a resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.Rule], tags: [OpenAPITags.PublicResource, OpenAPITags.Rule],
request: { request: {
params: listResourceRulesParamsSchema, params: listResourceRulesParamsSchema,
@@ -38,32 +38,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/resource/{resourceId}/users", path: "/resource/{resourceId}/users",
description: "List all users for a resource.", description: "List all users for a resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: listResourceUsersSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/public-resource/{resourceId}/users",
description: "List all users for a resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.User], tags: [OpenAPITags.PublicResource, OpenAPITags.User],
request: { request: {
params: listResourceUsersSchema params: listResourceUsersSchema
+14 -29
View File
@@ -138,6 +138,15 @@ const listResourcesSchema = z.strictObject({
description: description:
"When set, only resources that have at least one target on this site are returned" "When set, only resources that have at least one target on this site are returned"
}), }),
status: z
.enum(["pending", "approved"])
.optional()
.catch(undefined)
.openapi({
type: "string",
enum: ["pending", "approved"],
description: "Filter by resource status"
}),
labels: z labels: z
.preprocess((val) => { .preprocess((val) => {
if (val === undefined || val === null || val === "") { if (val === undefined || val === null || val === "") {
@@ -400,35 +409,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/org/{orgId}/resources", path: "/org/{orgId}/resources",
description: "List resources for an organization.", description: "List resources for an organization.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: z.object({
orgId: z.string()
}),
query: listResourcesSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/org/{orgId}/public-resources",
description: "List resources for an organization.",
tags: [OpenAPITags.PublicResource], tags: [OpenAPITags.PublicResource],
request: { request: {
params: z.object({ params: z.object({
@@ -480,6 +460,7 @@ export async function listResources(
sort_by, sort_by,
order, order,
siteId, siteId,
status,
labels: labelFilter labels: labelFilter
} = parsedQuery.data; } = parsedQuery.data;
@@ -689,6 +670,10 @@ export async function listResources(
} }
} }
if (typeof status !== "undefined") {
conditions.push(eq(resources.status, status));
}
if (siteId != null) { if (siteId != null) {
const resourcesWithSite = db const resourcesWithSite = db
.select({ resourceId: targets.resourceId }) .select({ resourceId: targets.resourceId })
@@ -45,18 +45,19 @@ function userResourceAliasesCacheKey(
page: number, page: number,
pageSize: number, pageSize: number,
includeLabels: boolean, includeLabels: boolean,
labelFilter: string[] labelFilter: string[],
status?: "pending" | "approved"
) { ) {
const labelsKey = const labelsKey =
labelFilter.length > 0 ? labelFilter.slice().sort().join(",") : "all"; labelFilter.length > 0 ? labelFilter.slice().sort().join(",") : "all";
return `userResourceAliases:${orgId}:${userId}:${page}:${pageSize}:${includeLabels ? "labels" : "plain"}:${labelsKey}`; return `userResourceAliases:${orgId}:${userId}:${page}:${pageSize}:${includeLabels ? "labels" : "plain"}:${labelsKey}:${status ?? "all"}`;
} }
const listUserResourceAliasesParamsSchema = z.strictObject({ const listUserResourceAliasesParamsSchema = z.strictObject({
orgId: z.string() orgId: z.string()
}); });
const listUserResourceAliasesQuerySchema = z.strictObject({ const listUserResourceAliasesQuerySchema = z.object({
pageSize: z.coerce pageSize: z.coerce
.number<string>() .number<string>()
.int() .int()
@@ -96,7 +97,16 @@ const listUserResourceAliasesQuerySchema = z.strictObject({
type: "array", type: "array",
description: description:
"Filter by resource labels. A resource matches when it has any of the given labels (OR)." "Filter by resource labels. A resource matches when it has any of the given labels (OR)."
}) }),
status: z
.enum(["pending", "approved"])
.optional()
.catch(undefined)
.openapi({
type: "string",
enum: ["pending", "approved"],
description: "Filter by site resource status"
})
}); });
export type UserResourceAliasItem = { export type UserResourceAliasItem = {
@@ -130,7 +140,8 @@ export async function listUserResourceAliases(
page, page,
pageSize, pageSize,
includeLabels, includeLabels,
labels: labelFilter labels: labelFilter,
status
} = parsedQuery.data; } = parsedQuery.data;
const parsedParams = listUserResourceAliasesParamsSchema.safeParse( const parsedParams = listUserResourceAliasesParamsSchema.safeParse(
@@ -172,7 +183,8 @@ export async function listUserResourceAliases(
page, page,
pageSize, pageSize,
includeLabels, includeLabels,
labelFilter ?? [] labelFilter ?? [],
status
); );
const cachedData: ListUserResourceAliasesResponse | undefined = const cachedData: ListUserResourceAliasesResponse | undefined =
await cache.get(cacheKey); await cache.get(cacheKey);
@@ -257,6 +269,10 @@ export async function listUserResourceAliases(
inArray(siteResources.siteResourceId, accessibleSiteResourceIds) inArray(siteResources.siteResourceId, accessibleSiteResourceIds)
]; ];
if (typeof status !== "undefined") {
whereConditions.push(eq(siteResources.status, status));
}
if (labelFilter && labelFilter.length > 0) { if (labelFilter && labelFilter.length > 0) {
whereConditions.push( whereConditions.push(
inArray( inArray(
@@ -34,39 +34,6 @@ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/whitelist/remove", path: "/resource/{resourceId}/whitelist/remove",
description: "Remove a single email from the resource whitelist.", description: "Remove a single email from the resource whitelist.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: removeEmailFromResourceWhitelistParamsSchema,
body: {
content: {
"application/json": {
schema: removeEmailFromResourceWhitelistBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/whitelist/remove",
description: "Remove a single email from the resource whitelist.",
tags: [OpenAPITags.PublicResource], tags: [OpenAPITags.PublicResource],
request: { request: {
params: removeEmailFromResourceWhitelistParamsSchema, params: removeEmailFromResourceWhitelistParamsSchema,
@@ -176,7 +143,10 @@ export async function removeEmailFromResourceWhitelist(
.from(resourcePolicyWhiteList) .from(resourcePolicyWhiteList)
.where( .where(
and( and(
eq(resourcePolicyWhiteList.resourcePolicyId, policyId), eq(
resourcePolicyWhiteList.resourcePolicyId,
policyId
),
eq(resourcePolicyWhiteList.email, email) eq(resourcePolicyWhiteList.email, email)
) )
); );
@@ -194,7 +164,10 @@ export async function removeEmailFromResourceWhitelist(
.delete(resourcePolicyWhiteList) .delete(resourcePolicyWhiteList)
.where( .where(
and( and(
eq(resourcePolicyWhiteList.resourcePolicyId, policyId), eq(
resourcePolicyWhiteList.resourcePolicyId,
policyId
),
eq(resourcePolicyWhiteList.email, email) eq(resourcePolicyWhiteList.email, email)
) )
); );
@@ -29,39 +29,6 @@ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/roles/remove", path: "/resource/{resourceId}/roles/remove",
description: "Remove a single role from a resource.", description: "Remove a single role from a resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: removeRoleFromResourceParamsSchema,
body: {
content: {
"application/json": {
schema: removeRoleFromResourceBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/roles/remove",
description: "Remove a single role from a resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.Role], tags: [OpenAPITags.PublicResource, OpenAPITags.Role],
request: { request: {
params: removeRoleFromResourceParamsSchema, params: removeRoleFromResourceParamsSchema,
@@ -29,39 +29,6 @@ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/users/remove", path: "/resource/{resourceId}/users/remove",
description: "Remove a single user from a resource.", description: "Remove a single user from a resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: removeUserFromResourceParamsSchema,
body: {
content: {
"application/json": {
schema: removeUserFromResourceBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/users/remove",
description: "Remove a single user from a resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.User], tags: [OpenAPITags.PublicResource, OpenAPITags.User],
request: { request: {
params: removeUserFromResourceParamsSchema, params: removeUserFromResourceParamsSchema,
@@ -29,40 +29,6 @@ const setResourceAuthMethodsBodySchema = z.strictObject({
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/header-auth", path: "/resource/{resourceId}/header-auth",
description:
"Set or update the header authentication for a resource. If user and password is not provided, it will remove the header authentication.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: setResourceAuthMethodsParamsSchema,
body: {
content: {
"application/json": {
schema: setResourceAuthMethodsBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/header-auth",
description: description:
"Set or update the header authentication for a resource. If user and password is not provided, it will remove the header authentication.", "Set or update the header authentication for a resource. If user and password is not provided, it will remove the header authentication.",
tags: [OpenAPITags.PublicResource], tags: [OpenAPITags.PublicResource],
@@ -27,40 +27,6 @@ const setResourceAuthMethodsBodySchema = z.strictObject({
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/password", path: "/resource/{resourceId}/password",
description:
"Set the password for a resource. Setting the password to null will remove it.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: setResourceAuthMethodsParamsSchema,
body: {
content: {
"application/json": {
schema: setResourceAuthMethodsBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/password",
description: description:
"Set the password for a resource. Setting the password to null will remove it.", "Set the password for a resource. Setting the password to null will remove it.",
tags: [OpenAPITags.PublicResource], tags: [OpenAPITags.PublicResource],
@@ -27,40 +27,6 @@ const setResourceAuthMethodsBodySchema = z.strictObject({
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/pincode", path: "/resource/{resourceId}/pincode",
description:
"Set the PIN code for a resource. Setting the PIN code to null will remove it.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: setResourceAuthMethodsParamsSchema,
body: {
content: {
"application/json": {
schema: setResourceAuthMethodsBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/pincode",
description: description:
"Set the PIN code for a resource. Setting the PIN code to null will remove it.", "Set the PIN code for a resource. Setting the PIN code to null will remove it.",
tags: [OpenAPITags.PublicResource], tags: [OpenAPITags.PublicResource],
@@ -21,40 +21,6 @@ const setResourceRolesParamsSchema = z.strictObject({
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/roles", path: "/resource/{resourceId}/roles",
description:
"Set roles for a resource. This will replace all existing roles. When the resource has an inline policy defined (no shared resource policy assigned), roles are set on the inline policy instead of directly on the resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: setResourceRolesParamsSchema,
body: {
content: {
"application/json": {
schema: setResourceRolesBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/roles",
description: description:
"Set roles for a resource. This will replace all existing roles. When the resource has an inline policy defined (no shared resource policy assigned), roles are set on the inline policy instead of directly on the resource.", "Set roles for a resource. This will replace all existing roles. When the resource has an inline policy defined (no shared resource policy assigned), roles are set on the inline policy instead of directly on the resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.Role], tags: [OpenAPITags.PublicResource, OpenAPITags.Role],
@@ -21,40 +21,6 @@ const setUserResourcesParamsSchema = z.strictObject({
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/users", path: "/resource/{resourceId}/users",
description:
"Set users for a resource. This will replace all existing users. When the resource has an inline policy defined (no shared resource policy assigned), users are set on the inline policy instead of directly on the resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: setUserResourcesParamsSchema,
body: {
content: {
"application/json": {
schema: setUserResourcesBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/users",
description: description:
"Set users for a resource. This will replace all existing users. When the resource has an inline policy defined (no shared resource policy assigned), users are set on the inline policy instead of directly on the resource.", "Set users for a resource. This will replace all existing users. When the resource has an inline policy defined (no shared resource policy assigned), users are set on the inline policy instead of directly on the resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.User], tags: [OpenAPITags.PublicResource, OpenAPITags.User],
@@ -35,40 +35,6 @@ const setResourceWhitelistParamsSchema = z.strictObject({
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/whitelist", path: "/resource/{resourceId}/whitelist",
description:
"Set email whitelist for a resource. This will replace all existing emails.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: setResourceWhitelistParamsSchema,
body: {
content: {
"application/json": {
schema: setResourceWhitelistBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/whitelist",
description: description:
"Set email whitelist for a resource. This will replace all existing emails.", "Set email whitelist for a resource. This will replace all existing emails.",
tags: [OpenAPITags.PublicResource], tags: [OpenAPITags.PublicResource],
-36
View File
@@ -240,42 +240,6 @@ const updateRawResourceBodySchema = z
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}", path: "/resource/{resourceId}",
description:
"Update a resource. Policy fields (sso, mfa, pincode, password, whitelist) update the inline policy when no shared resource policy is assigned; when a shared policy is assigned those fields override the shared policy for this resource only.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: updateResourceParamsSchema,
body: {
content: {
"application/json": {
schema: updateHttpResourceBodySchema.and(
updateRawResourceBodySchema
)
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}",
description: description:
"Update a resource. Policy fields (sso, mfa, pincode, password, whitelist) update the inline policy when no shared resource policy is assigned; when a shared policy is assigned those fields override the shared policy for this resource only.", "Update a resource. Policy fields (sso, mfa, pincode, password, whitelist) update the inline policy when no shared resource policy is assigned; when a shared policy is assigned those fields override the shared policy for this resource only.",
tags: [OpenAPITags.PublicResource], tags: [OpenAPITags.PublicResource],
@@ -49,39 +49,6 @@ registry.registerPath({
method: "post", method: "post",
path: "/resource/{resourceId}/rule/{ruleId}", path: "/resource/{resourceId}/rule/{ruleId}",
description: "Update a resource rule.", description: "Update a resource rule.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: updateResourceRuleParamsSchema,
body: {
content: {
"application/json": {
schema: updateResourceRuleSchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/public-resource/{resourceId}/rule/{ruleId}",
description: "Update a resource rule.",
tags: [OpenAPITags.PublicResource, OpenAPITags.Rule], tags: [OpenAPITags.PublicResource, OpenAPITags.Rule],
request: { request: {
params: updateResourceRuleParamsSchema, params: updateResourceRuleParamsSchema,
+251
View File
@@ -0,0 +1,251 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import {
db,
resources,
siteNetworks,
siteResources,
sites,
type Site,
type SiteResource
} from "@server/db";
import { and, eq, inArray } from "drizzle-orm";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
import {
getResourceIdsForSite,
getSiteResourceIdsForSite
} from "@server/lib/deleteSiteAssociatedResources";
import {
handleMessagingForUpdatedSiteResource,
rebuildClientAssociationsFromSiteResource,
waitForSiteResourceRebuildIdle
} from "@server/lib/rebuildClientAssociations";
const approveSiteParamsSchema = z.strictObject({
siteId: z.coerce.number().int().positive()
});
registry.registerPath({
method: "post",
path: "/site/{siteId}/approve",
description:
"Approve a pending site and approve (and enable when needed) its associated resources.",
tags: [OpenAPITags.Site],
request: {
params: approveSiteParamsSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
type SiteResourceEnableSideEffect = {
existing: SiteResource;
updated: SiteResource;
siteIds: number[];
};
export async function approveSite(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = approveSiteParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedParams.error).toString()
)
);
}
const { siteId } = parsedParams.data;
const [existingSite] = await db
.select()
.from(sites)
.where(eq(sites.siteId, siteId))
.limit(1);
if (!existingSite) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Site with ID ${siteId} not found`
)
);
}
if (!existingSite.orgId) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
`Site with ID ${siteId} has no organization`
)
);
}
const orgId = existingSite.orgId;
let updatedSite: Site | undefined;
const siteResourceEnableSideEffects: SiteResourceEnableSideEffect[] =
[];
await db.transaction(async (trx) => {
[updatedSite] = await trx
.update(sites)
.set({ status: "approved" })
.where(eq(sites.siteId, siteId))
.returning();
const resourceIds = await getResourceIdsForSite(siteId, trx);
const siteResourceIds = await getSiteResourceIdsForSite(
siteId,
orgId,
trx
);
if (resourceIds.length > 0) {
const pendingDisabledResources = await trx
.select({ resourceId: resources.resourceId })
.from(resources)
.where(
and(
inArray(resources.resourceId, resourceIds),
eq(resources.status, "pending"),
eq(resources.enabled, false)
)
);
await trx
.update(resources)
.set({ status: "approved" })
.where(inArray(resources.resourceId, resourceIds));
if (pendingDisabledResources.length > 0) {
await trx
.update(resources)
.set({ enabled: true })
.where(
inArray(
resources.resourceId,
pendingDisabledResources.map(
(r) => r.resourceId
)
)
);
}
}
if (siteResourceIds.length > 0) {
const existingSiteResources = await trx
.select()
.from(siteResources)
.where(
inArray(siteResources.siteResourceId, siteResourceIds)
);
const pendingDisabledSiteResources =
existingSiteResources.filter(
(sr) => sr.status === "pending" && !sr.enabled
);
await trx
.update(siteResources)
.set({ status: "approved" })
.where(
inArray(siteResources.siteResourceId, siteResourceIds)
);
if (pendingDisabledSiteResources.length > 0) {
const enableIds = pendingDisabledSiteResources.map(
(sr) => sr.siteResourceId
);
const updatedEnabledSiteResources = await trx
.update(siteResources)
.set({ enabled: true })
.where(inArray(siteResources.siteResourceId, enableIds))
.returning();
for (const updated of updatedEnabledSiteResources) {
const existing = pendingDisabledSiteResources.find(
(sr) => sr.siteResourceId === updated.siteResourceId
);
if (!existing || !updated.networkId) {
continue;
}
const networkSites = await trx
.select({ siteId: siteNetworks.siteId })
.from(siteNetworks)
.where(
eq(siteNetworks.networkId, updated.networkId)
);
siteResourceEnableSideEffects.push({
existing,
updated,
siteIds: networkSites.map((s) => s.siteId)
});
}
}
}
});
for (const sideEffect of siteResourceEnableSideEffects) {
rebuildClientAssociationsFromSiteResource(sideEffect.updated)
.then(() =>
waitForSiteResourceRebuildIdle(
sideEffect.updated.siteResourceId
)
)
.then(() =>
handleMessagingForUpdatedSiteResource(
sideEffect.existing,
sideEffect.updated,
sideEffect.siteIds,
sideEffect.siteIds
)
)
.catch((e) => {
logger.error(
`Failed to rebuild and handle messaging for site resource ${sideEffect.updated.siteResourceId} after site approval:`,
e
);
});
}
return response(res, {
data: updatedSite ?? null,
success: true,
error: false,
message: "Site approved successfully",
status: HttpCode.OK
});
} catch (error) {
logger.error(error);
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}
+9 -9
View File
@@ -159,15 +159,21 @@ export async function deleteSite(
siteResources: [] siteResources: []
}; };
await db.transaction(async (trx) => { if (deleteResources) {
if (deleteResources) { await db.transaction(async (trx) => {
resourceSideEffects = await deleteAssociatedResourcesForSite( resourceSideEffects = await deleteAssociatedResourcesForSite(
siteId, siteId,
site.orgId, site.orgId,
trx trx
); );
} });
await runDeleteSiteAssociatedResourcesSideEffects(
resourceSideEffects
);
}
await db.transaction(async (trx) => {
if (site.type == "wireguard") { if (site.type == "wireguard") {
if (site.pubKey) { if (site.pubKey) {
await deletePeer(site.exitNodeId!, site.pubKey); await deletePeer(site.exitNodeId!, site.pubKey);
@@ -180,12 +186,6 @@ export async function deleteSite(
await usageService.add(site.orgId, LimitId.SITES, -1, trx); await usageService.add(site.orgId, LimitId.SITES, -1, trx);
}); });
if (deleteResources) {
await runDeleteSiteAssociatedResourcesSideEffects(
resourceSideEffects
);
}
if (deletedNewt) { if (deletedNewt) {
const payload = { const payload = {
type: `newt/wg/terminate`, type: `newt/wg/terminate`,
+2
View File
@@ -3,6 +3,8 @@ export * from "./getStatusHistory";
export * from "./createSite"; export * from "./createSite";
export * from "./deleteSite"; export * from "./deleteSite";
export * from "./updateSite"; export * from "./updateSite";
export * from "./approveSite";
export * from "./rejectSite";
export * from "./listSites"; export * from "./listSites";
export * from "./listSiteRoles"; export * from "./listSiteRoles";
export * from "./pickSiteDefaults"; export * from "./pickSiteDefaults";
+1 -1
View File
@@ -177,7 +177,7 @@ registry.registerPath({
method: "get", method: "get",
path: "/org/{orgId}/sites", path: "/org/{orgId}/sites",
description: "List all sites in an organization", description: "List all sites in an organization",
tags: [OpenAPITags.Site], tags: [OpenAPITags.Org, OpenAPITags.Site],
request: { request: {
params: listSitesParamsSchema, params: listSitesParamsSchema,
query: listSitesSchema query: listSitesSchema
+178
View File
@@ -0,0 +1,178 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { newts, sites } from "@server/db";
import { eq } from "drizzle-orm";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import logger from "@server/logger";
import { deletePeer } from "../gerbil/peers";
import { fromError } from "zod-validation-error";
import { sendToClient } from "#dynamic/routers/ws";
import { OpenAPITags, registry } from "@server/openApi";
import { cleanupSiteAssociations } from "@server/lib/rebuildClientAssociations";
import { usageService } from "@server/lib/billing/usageService";
import { LimitId } from "@server/lib/billing";
import {
deletePendingAssociatedResourcesForSite,
exceedsSiteAssociatedResourceDeleteLimit,
getPendingAssociatedResourceCountForSite,
runDeleteSiteAssociatedResourcesSideEffects,
MAX_SITE_ASSOCIATED_RESOURCES_FOR_BULK_DELETE,
type DeleteSiteAssociatedResourcesSideEffects
} from "@server/lib/deleteSiteAssociatedResources";
const rejectSiteParamsSchema = z.strictObject({
siteId: z.coerce.number().int().positive()
});
registry.registerPath({
method: "post",
path: "/site/{siteId}/reject",
description:
"Reject a pending site by deleting it and any associated resources that are still pending.",
tags: [OpenAPITags.Site],
request: {
params: rejectSiteParamsSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
export async function rejectSite(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedParams = rejectSiteParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedParams.error).toString()
)
);
}
const { siteId } = parsedParams.data;
const [site] = await db
.select()
.from(sites)
.where(eq(sites.siteId, siteId))
.limit(1);
if (!site) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Site with ID ${siteId} not found`
)
);
}
if (!site.orgId) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
`Site with ID ${siteId} has no organization`
)
);
}
const pendingAssociatedResourceCount =
await getPendingAssociatedResourceCountForSite(siteId, site.orgId);
if (
exceedsSiteAssociatedResourceDeleteLimit(
pendingAssociatedResourceCount
)
) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
`Cannot reject site and associated pending resources when the site has more than ${MAX_SITE_ASSOCIATED_RESOURCES_FOR_BULK_DELETE} pending resources`
)
);
}
const [deletedNewt] = await db
.select()
.from(newts)
.where(eq(newts.siteId, siteId))
.limit(1);
let resourceSideEffects: DeleteSiteAssociatedResourcesSideEffects = {
resources: [],
siteResources: []
};
await db.transaction(async (trx) => {
resourceSideEffects = await deletePendingAssociatedResourcesForSite(
siteId,
site.orgId,
trx
);
});
await runDeleteSiteAssociatedResourcesSideEffects(resourceSideEffects);
await db.transaction(async (trx) => {
if (site.type == "wireguard") {
if (site.pubKey) {
await deletePeer(site.exitNodeId!, site.pubKey);
}
} else if (site.type == "newt") {
await cleanupSiteAssociations(site, trx);
}
await trx.delete(sites).where(eq(sites.siteId, siteId));
await usageService.add(site.orgId, LimitId.SITES, -1, trx);
});
if (deletedNewt) {
const payload = {
type: `newt/wg/terminate`,
data: {}
};
sendToClient(deletedNewt.newtId, payload).catch((error) => {
logger.error(
"Failed to send termination message to newt:",
error
);
});
}
return response(res, {
data: null,
success: true,
error: false,
message: "Site rejected successfully",
status: HttpCode.OK
});
} catch (error) {
logger.error(error);
if (createHttpError.isHttpError(error)) {
return next(error);
}
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}
-1
View File
@@ -21,7 +21,6 @@ const updateSiteBodySchema = z
name: z.string().min(1).max(255).optional(), name: z.string().min(1).max(255).optional(),
niceId: z.string().min(1).max(255).optional(), niceId: z.string().min(1).max(255).optional(),
dockerSocketEnabled: z.boolean().optional(), dockerSocketEnabled: z.boolean().optional(),
status: z.enum(["pending", "approved"]).optional(),
autoUpdateEnabled: z.boolean().optional(), autoUpdateEnabled: z.boolean().optional(),
autoUpdateOverrideOrg: z.boolean().optional() autoUpdateOverrideOrg: z.boolean().optional()
}) })
@@ -31,40 +31,6 @@ const addClientToSiteResourceParamsSchema = z
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/site-resource/{siteResourceId}/clients/add", path: "/site-resource/{siteResourceId}/clients/add",
description:
"Add a single client to a site resource. Clients with a userId cannot be added.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: addClientToSiteResourceParamsSchema,
body: {
content: {
"application/json": {
schema: addClientToSiteResourceBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/private-resource/{siteResourceId}/clients/add",
description: description:
"Add a single client to a site resource. Clients with a userId cannot be added.", "Add a single client to a site resource. Clients with a userId cannot be added.",
tags: [OpenAPITags.PrivateResource, OpenAPITags.Client], tags: [OpenAPITags.PrivateResource, OpenAPITags.Client],
@@ -33,39 +33,6 @@ registry.registerPath({
method: "post", method: "post",
path: "/site-resource/{siteResourceId}/roles/add", path: "/site-resource/{siteResourceId}/roles/add",
description: "Add a single role to a site resource.", description: "Add a single role to a site resource.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: addRoleToSiteResourceParamsSchema,
body: {
content: {
"application/json": {
schema: addRoleToSiteResourceBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/private-resource/{siteResourceId}/roles/add",
description: "Add a single role to a site resource.",
tags: [OpenAPITags.PrivateResource, OpenAPITags.Role], tags: [OpenAPITags.PrivateResource, OpenAPITags.Role],
request: { request: {
params: addRoleToSiteResourceParamsSchema, params: addRoleToSiteResourceParamsSchema,
@@ -33,39 +33,6 @@ registry.registerPath({
method: "post", method: "post",
path: "/site-resource/{siteResourceId}/users/add", path: "/site-resource/{siteResourceId}/users/add",
description: "Add a single user to a site resource.", description: "Add a single user to a site resource.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: addUserToSiteResourceParamsSchema,
body: {
content: {
"application/json": {
schema: addUserToSiteResourceBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/private-resource/{siteResourceId}/users/add",
description: "Add a single user to a site resource.",
tags: [OpenAPITags.PrivateResource, OpenAPITags.User], tags: [OpenAPITags.PrivateResource, OpenAPITags.User],
request: { request: {
params: addUserToSiteResourceParamsSchema, params: addUserToSiteResourceParamsSchema,
@@ -38,39 +38,6 @@ registry.registerPath({
method: "post", method: "post",
path: "/client/{clientId}/site-resources", path: "/client/{clientId}/site-resources",
description: "Add a machine client to multiple site resources at once.", description: "Add a machine client to multiple site resources at once.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: batchAddClientToSiteResourcesParamsSchema,
body: {
content: {
"application/json": {
schema: batchAddClientToSiteResourcesBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/client/{clientId}/private-resources",
description: "Add a machine client to multiple site resources at once.",
tags: [OpenAPITags.Client], tags: [OpenAPITags.Client],
request: { request: {
params: batchAddClientToSiteResourcesParamsSchema, params: batchAddClientToSiteResourcesParamsSchema,
@@ -56,7 +56,6 @@ const createSiteResourceSchema = z
siteId: z.number().int().positive().optional(), // DEPRECATED: for backward compatibility, we will convert this to siteIds array if provided siteId: z.number().int().positive().optional(), // DEPRECATED: for backward compatibility, we will convert this to siteIds array if provided
destinationPort: z.int().positive().optional(), destinationPort: z.int().positive().optional(),
destination: z.string().min(1).nullish(), destination: z.string().min(1).nullish(),
enabled: z.boolean().default(true),
alias: z alias: z
.string() .string()
.regex( .regex(
@@ -208,39 +207,6 @@ registry.registerPath({
method: "put", method: "put",
path: "/org/{orgId}/site-resource", path: "/org/{orgId}/site-resource",
description: "Create a new site resource.", description: "Create a new site resource.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: createSiteResourceParamsSchema,
body: {
content: {
"application/json": {
schema: createSiteResourceSchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "put",
path: "/org/{orgId}/private-resource",
description: "Create a new site resource.",
tags: [OpenAPITags.PrivateResource], tags: [OpenAPITags.PrivateResource],
request: { request: {
params: createSiteResourceParamsSchema, params: createSiteResourceParamsSchema,
@@ -308,7 +274,6 @@ export async function createSiteResource(
scheme, scheme,
destinationPort, destinationPort,
destination, destination,
enabled,
ssl, ssl,
alias, alias,
userIds, userIds,
@@ -572,7 +537,6 @@ export async function createSiteResource(
destination: destination, // the ssh can be null destination: destination, // the ssh can be null
scheme, scheme,
destinationPort, destinationPort,
enabled,
alias: alias ? alias.trim() : null, alias: alias ? alias.trim() : null,
aliasAddress, aliasAddress,
tcpPortRangeString: tcpPortRangeStringAdjusted, tcpPortRangeString: tcpPortRangeStringAdjusted,
@@ -27,32 +27,6 @@ registry.registerPath({
method: "delete", method: "delete",
path: "/site-resource/{siteResourceId}", path: "/site-resource/{siteResourceId}",
description: "Delete a site resource.", description: "Delete a site resource.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: deleteSiteResourceParamsSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "delete",
path: "/private-resource/{siteResourceId}",
description: "Delete a site resource.",
tags: [OpenAPITags.PrivateResource], tags: [OpenAPITags.PrivateResource],
request: { request: {
params: deleteSiteResourceParamsSchema params: deleteSiteResourceParamsSchema
@@ -57,36 +57,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/site-resource/{siteResourceId}", path: "/site-resource/{siteResourceId}",
description: "Get a specific site resource by siteResourceId.", description: "Get a specific site resource by siteResourceId.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: z.object({
siteResourceId: z.number(),
siteId: z.number(),
orgId: z.string()
})
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/private-resource/{siteResourceId}",
description: "Get a specific site resource by siteResourceId.",
tags: [OpenAPITags.PrivateResource], tags: [OpenAPITags.PrivateResource],
request: { request: {
params: z.object({ params: z.object({
@@ -86,6 +86,15 @@ const listAllSiteResourcesByOrgQuerySchema = z.strictObject({
description: description:
"When set, only site resources associated with this site (via network) are returned" "When set, only site resources associated with this site (via network) are returned"
}), }),
status: z
.enum(["pending", "approved"])
.optional()
.catch(undefined)
.openapi({
type: "string",
enum: ["pending", "approved"],
description: "Filter by site resource status"
}),
labels: z labels: z
.preprocess((val) => { .preprocess((val) => {
if (val === undefined || val === null || val === "") { if (val === undefined || val === null || val === "") {
@@ -221,33 +230,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/org/{orgId}/site-resources", path: "/org/{orgId}/site-resources",
description: "List all site resources for an organization.", description: "List all site resources for an organization.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: listAllSiteResourcesByOrgParamsSchema,
query: listAllSiteResourcesByOrgQuerySchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/org/{orgId}/private-resources",
description: "List all site resources for an organization.",
tags: [OpenAPITags.PrivateResource], tags: [OpenAPITags.PrivateResource],
request: { request: {
params: listAllSiteResourcesByOrgParamsSchema, params: listAllSiteResourcesByOrgParamsSchema,
@@ -310,6 +292,7 @@ export async function listAllSiteResourcesByOrg(
sort_by, sort_by,
order, order,
siteId, siteId,
status,
labels: labelFilter labels: labelFilter
} = parsedQuery.data; } = parsedQuery.data;
@@ -342,6 +325,10 @@ export async function listAllSiteResourcesByOrg(
conditions.push(eq(siteResources.mode, mode)); conditions.push(eq(siteResources.mode, mode));
} }
if (typeof status !== "undefined") {
conditions.push(eq(siteResources.status, status));
}
if (labelFilter && labelFilter.length > 0) { if (labelFilter && labelFilter.length > 0) {
conditions.push( conditions.push(
inArray( inArray(
@@ -39,32 +39,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/site-resource/{siteResourceId}/clients", path: "/site-resource/{siteResourceId}/clients",
description: "List all clients for a site resource.", description: "List all clients for a site resource.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: listSiteResourceClientsSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/private-resource/{siteResourceId}/clients",
description: "List all clients for a site resource.",
tags: [OpenAPITags.PrivateResource, OpenAPITags.Client], tags: [OpenAPITags.PrivateResource, OpenAPITags.Client],
request: { request: {
params: listSiteResourceClientsSchema params: listSiteResourceClientsSchema
@@ -40,32 +40,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/site-resource/{siteResourceId}/roles", path: "/site-resource/{siteResourceId}/roles",
description: "List all roles for a site resource.", description: "List all roles for a site resource.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: listSiteResourceRolesSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/private-resource/{siteResourceId}/roles",
description: "List all roles for a site resource.",
tags: [OpenAPITags.PrivateResource, OpenAPITags.Role], tags: [OpenAPITags.PrivateResource, OpenAPITags.Role],
request: { request: {
params: listSiteResourceRolesSchema params: listSiteResourceRolesSchema
@@ -43,32 +43,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/site-resource/{siteResourceId}/users", path: "/site-resource/{siteResourceId}/users",
description: "List all users for a site resource.", description: "List all users for a site resource.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: listSiteResourceUsersSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/private-resource/{siteResourceId}/users",
description: "List all users for a site resource.",
tags: [OpenAPITags.PrivateResource, OpenAPITags.User], tags: [OpenAPITags.PrivateResource, OpenAPITags.User],
request: { request: {
params: listSiteResourceUsersSchema params: listSiteResourceUsersSchema
@@ -47,6 +47,15 @@ const listSiteResourcesQuerySchema = z.strictObject({
enum: ["asc", "desc"], enum: ["asc", "desc"],
default: "asc", default: "asc",
description: "Sort order" description: "Sort order"
}),
status: z
.enum(["pending", "approved"])
.optional()
.catch(undefined)
.openapi({
type: "string",
enum: ["pending", "approved"],
description: "Filter by site resource status"
}) })
}); });
@@ -58,33 +67,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/org/{orgId}/site/{siteId}/resources", path: "/org/{orgId}/site/{siteId}/resources",
description: "List site resources for a site.", description: "List site resources for a site.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: listSiteResourcesParamsSchema,
query: listSiteResourcesQuerySchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/org/{orgId}/site/{siteId}/private-resources",
description: "List site resources for a site.",
tags: [OpenAPITags.PrivateResource], tags: [OpenAPITags.PrivateResource],
request: { request: {
params: listSiteResourcesParamsSchema, params: listSiteResourcesParamsSchema,
@@ -137,7 +119,7 @@ export async function listSiteResources(
} }
const { siteId, orgId } = parsedParams.data; const { siteId, orgId } = parsedParams.data;
const { limit, offset, sort_by, order } = parsedQuery.data; const { limit, offset, sort_by, order, status } = parsedQuery.data;
// Verify the site exists and belongs to the org // Verify the site exists and belongs to the org
const site = await db const site = await db
@@ -151,6 +133,15 @@ export async function listSiteResources(
} }
// Get site resources by joining networks to siteResources via siteNetworks // Get site resources by joining networks to siteResources via siteNetworks
const conditions = [
eq(siteNetworks.siteId, siteId),
eq(siteResources.orgId, orgId)
];
if (typeof status !== "undefined") {
conditions.push(eq(siteResources.status, status));
}
const siteResourcesList = await db const siteResourcesList = await db
.select() .select()
.from(siteNetworks) .from(siteNetworks)
@@ -159,12 +150,7 @@ export async function listSiteResources(
siteResources, siteResources,
eq(siteResources.networkId, networks.networkId) eq(siteResources.networkId, networks.networkId)
) )
.where( .where(and(...conditions))
and(
eq(siteNetworks.siteId, siteId),
eq(siteResources.orgId, orgId)
)
)
.orderBy( .orderBy(
sort_by sort_by
? order === "asc" ? order === "asc"
@@ -31,40 +31,6 @@ const removeClientFromSiteResourceParamsSchema = z
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/site-resource/{siteResourceId}/clients/remove", path: "/site-resource/{siteResourceId}/clients/remove",
description:
"Remove a single client from a site resource. Clients with a userId cannot be removed.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: removeClientFromSiteResourceParamsSchema,
body: {
content: {
"application/json": {
schema: removeClientFromSiteResourceBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/private-resource/{siteResourceId}/clients/remove",
description: description:
"Remove a single client from a site resource. Clients with a userId cannot be removed.", "Remove a single client from a site resource. Clients with a userId cannot be removed.",
tags: [OpenAPITags.PrivateResource, OpenAPITags.Client], tags: [OpenAPITags.PrivateResource, OpenAPITags.Client],
@@ -33,39 +33,6 @@ registry.registerPath({
method: "post", method: "post",
path: "/site-resource/{siteResourceId}/roles/remove", path: "/site-resource/{siteResourceId}/roles/remove",
description: "Remove a single role from a site resource.", description: "Remove a single role from a site resource.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: removeRoleFromSiteResourceParamsSchema,
body: {
content: {
"application/json": {
schema: removeRoleFromSiteResourceBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/private-resource/{siteResourceId}/roles/remove",
description: "Remove a single role from a site resource.",
tags: [OpenAPITags.PrivateResource, OpenAPITags.Role], tags: [OpenAPITags.PrivateResource, OpenAPITags.Role],
request: { request: {
params: removeRoleFromSiteResourceParamsSchema, params: removeRoleFromSiteResourceParamsSchema,
@@ -33,39 +33,6 @@ registry.registerPath({
method: "post", method: "post",
path: "/site-resource/{siteResourceId}/users/remove", path: "/site-resource/{siteResourceId}/users/remove",
description: "Remove a single user from a site resource.", description: "Remove a single user from a site resource.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: removeUserFromSiteResourceParamsSchema,
body: {
content: {
"application/json": {
schema: removeUserFromSiteResourceBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/private-resource/{siteResourceId}/users/remove",
description: "Remove a single user from a site resource.",
tags: [OpenAPITags.PrivateResource, OpenAPITags.User], tags: [OpenAPITags.PrivateResource, OpenAPITags.User],
request: { request: {
params: removeUserFromSiteResourceParamsSchema, params: removeUserFromSiteResourceParamsSchema,
@@ -31,40 +31,6 @@ const setSiteResourceClientsParamsSchema = z
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/site-resource/{siteResourceId}/clients", path: "/site-resource/{siteResourceId}/clients",
description:
"Set clients for a site resource. This will replace all existing clients. Clients with a userId cannot be added.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: setSiteResourceClientsParamsSchema,
body: {
content: {
"application/json": {
schema: setSiteResourceClientsBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/private-resource/{siteResourceId}/clients",
description: description:
"Set clients for a site resource. This will replace all existing clients. Clients with a userId cannot be added.", "Set clients for a site resource. This will replace all existing clients. Clients with a userId cannot be added.",
tags: [OpenAPITags.PrivateResource, OpenAPITags.Client], tags: [OpenAPITags.PrivateResource, OpenAPITags.Client],
@@ -32,40 +32,6 @@ const setSiteResourceRolesParamsSchema = z
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/site-resource/{siteResourceId}/roles", path: "/site-resource/{siteResourceId}/roles",
description:
"Set roles for a site resource. This will replace all existing roles.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: setSiteResourceRolesParamsSchema,
body: {
content: {
"application/json": {
schema: setSiteResourceRolesBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/private-resource/{siteResourceId}/roles",
description: description:
"Set roles for a site resource. This will replace all existing roles.", "Set roles for a site resource. This will replace all existing roles.",
tags: [OpenAPITags.PrivateResource, OpenAPITags.Role], tags: [OpenAPITags.PrivateResource, OpenAPITags.Role],
@@ -33,40 +33,6 @@ const setSiteResourceUsersParamsSchema = z
registry.registerPath({ registry.registerPath({
method: "post", method: "post",
path: "/site-resource/{siteResourceId}/users", path: "/site-resource/{siteResourceId}/users",
description:
"Set users for a site resource. This will replace all existing users.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: setSiteResourceUsersParamsSchema,
body: {
content: {
"application/json": {
schema: setSiteResourceUsersBodySchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/private-resource/{siteResourceId}/users",
description: description:
"Set users for a site resource. This will replace all existing users.", "Set users for a site resource. This will replace all existing users.",
tags: [OpenAPITags.PrivateResource, OpenAPITags.User], tags: [OpenAPITags.PrivateResource, OpenAPITags.User],
@@ -152,6 +152,11 @@ const updateSiteResourceSchema = z
) )
.refine( .refine(
(data) => { (data) => {
// this is a partial update; only enforce destination when the
// caller is actually changing mode or destination
if (data.mode === undefined && data.destination === undefined) {
return true;
}
// destination is only optional for ssh mode with native authDaemonMode // destination is only optional for ssh mode with native authDaemonMode
if (data.mode === "ssh" && data.authDaemonMode === "native") { if (data.mode === "ssh" && data.authDaemonMode === "native") {
return true; return true;
@@ -208,39 +213,6 @@ registry.registerPath({
method: "post", method: "post",
path: "/site-resource/{siteResourceId}", path: "/site-resource/{siteResourceId}",
description: "Update a site resource.", description: "Update a site resource.",
tags: [OpenAPITags.PrivateResourceLegacy],
request: {
params: updateSiteResourceParamsSchema,
body: {
content: {
"application/json": {
schema: updateSiteResourceSchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "post",
path: "/private-resource/{siteResourceId}",
description: "Update a site resource.",
tags: [OpenAPITags.PrivateResource], tags: [OpenAPITags.PrivateResource],
request: { request: {
params: updateSiteResourceParamsSchema, params: updateSiteResourceParamsSchema,
@@ -444,8 +416,10 @@ export async function updateSiteResource(
: []; : [];
const existingSiteIds = existingSiteNetworks.map((sn) => sn.siteId); const existingSiteIds = existingSiteNetworks.map((sn) => sn.siteId);
let fullDomain: string | null = null; // undefined means "leave unchanged" (partial update); only nulled out
let finalSubdomain: string | null = null; // when the mode is explicitly being changed away from http
let fullDomain: string | null | undefined = undefined;
let finalSubdomain: string | null | undefined = undefined;
if (domainId) { if (domainId) {
// Validate domain and construct full domain // Validate domain and construct full domain
const domainResult = await validateAndConstructDomain( const domainResult = await validateAndConstructDomain(
@@ -481,6 +455,11 @@ export async function updateSiteResource(
) )
); );
} }
} else if (mode !== undefined && mode !== "http") {
// mode is explicitly changing away from http, so the resource
// can no longer have a domain associated with it
fullDomain = null;
finalSubdomain = null;
} }
// make sure the alias is unique within the org if provided // make sure the alias is unique within the org if provided
@@ -549,15 +528,28 @@ export async function updateSiteResource(
destination: destination, destination: destination,
destinationPort: destinationPort, destinationPort: destinationPort,
enabled: enabled, enabled: enabled,
alias: alias ? alias.trim() : null, alias:
alias !== undefined
? alias
? alias.trim()
: null
: mode !== undefined &&
mode !== "host" &&
mode !== "ssh"
? null
: undefined,
tcpPortRangeString: tcpPortRangeStringAdjusted, tcpPortRangeString: tcpPortRangeStringAdjusted,
udpPortRangeString: udpPortRangeString:
mode == "http" || mode == "ssh" mode == "http" || mode == "ssh"
? "" ? ""
: udpPortRangeString, : udpPortRangeString,
disableIcmp: disableIcmp:
disableIcmp || mode !== undefined
(mode == "http" || mode == "ssh" ? true : false), ? disableIcmp ||
(mode == "http" || mode == "ssh"
? true
: false)
: disableIcmp,
domainId, domainId,
subdomain: finalSubdomain, subdomain: finalSubdomain,
fullDomain, fullDomain,
-33
View File
@@ -93,39 +93,6 @@ registry.registerPath({
method: "put", method: "put",
path: "/resource/{resourceId}/target", path: "/resource/{resourceId}/target",
description: "Create a target for a resource.", description: "Create a target for a resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: createTargetParamsSchema,
body: {
content: {
"application/json": {
schema: createTargetSchema
}
}
}
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "put",
path: "/public-resource/{resourceId}/target",
description: "Create a target for a resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.Target], tags: [OpenAPITags.PublicResource, OpenAPITags.Target],
request: { request: {
params: createTargetParamsSchema, params: createTargetParamsSchema,
-27
View File
@@ -92,33 +92,6 @@ registry.registerPath({
method: "get", method: "get",
path: "/resource/{resourceId}/targets", path: "/resource/{resourceId}/targets",
description: "List targets for a resource.", description: "List targets for a resource.",
tags: [OpenAPITags.PublicResourceLegacy],
request: {
params: listTargetsParamsSchema,
query: listTargetsSchema
},
responses: {
200: {
description: "Successful response",
content: {
"application/json": {
schema: z.object({
data: z.record(z.string(), z.any()).nullable(),
success: z.boolean(),
error: z.boolean(),
message: z.string(),
status: z.number()
})
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/public-resource/{resourceId}/targets",
description: "List targets for a resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.Target], tags: [OpenAPITags.PublicResource, OpenAPITags.Target],
request: { request: {
params: listTargetsParamsSchema, params: listTargetsParamsSchema,

Some files were not shown because too many files have changed in this diff Show More