diff --git a/server/lib/blueprints/applyBlueprint.ts b/server/lib/blueprints/applyBlueprint.ts index 1edf87f80..c62dd4735 100644 --- a/server/lib/blueprints/applyBlueprint.ts +++ b/server/lib/blueprints/applyBlueprint.ts @@ -8,7 +8,7 @@ import { userSiteResources, clientSiteResources } from "@server/db"; -import { Config, ConfigSchema } from "./types"; +import { Config, ConfigSchema, isTargetsOnlyResource } from "./types"; import { PublicResourcesResults, updatePublicResources @@ -34,6 +34,12 @@ import { rebuildClientAssociationsFromSiteResource, waitForSiteResourceRebuildIdle } 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 = { orgId: string; @@ -64,6 +70,7 @@ export async function applyBlueprint({ let publicResourcesResults: PublicResourcesResults = []; let privateResourcesResults: ClientResourcesResults = []; + await db.transaction(async (trx) => { await updateResourcePolicies(orgId, config, trx); diff --git a/server/lib/blueprints/privateResources.ts b/server/lib/blueprints/privateResources.ts index 8d00701f1..74a1f618f 100644 --- a/server/lib/blueprints/privateResources.ts +++ b/server/lib/blueprints/privateResources.ts @@ -25,6 +25,12 @@ import { getNextAvailableAliasAddress } from "../ip"; import { createCertificate } from "#dynamic/routers/certificates/createCertificate"; import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; import { tierMatrix } from "../billing/tierMatrix"; +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"; async function getDomainForSiteResource( siteResourceId: number | undefined, @@ -413,6 +419,34 @@ export async function updatePrivateResources( oldSites: existingSiteIds }); } else { + // create a brand new resource + + if (build == "saas") { + const usage = await usageService.getUsage( + orgId, + LimitId.PRIVATE_RESOURCES + ); + if (!usage) { + throw new Error( + `Usage data not found for org ${orgId} and limit ${LimitId.PRIVATE_RESOURCES}` + ); + } + const rejectResource = await usageService.checkLimitSet( + orgId, + + LimitId.PRIVATE_RESOURCES, + { + ...usage, + instantaneousValue: (usage.instantaneousValue || 0) + 1 + } // We need to add one to know if we are violating the limit + ); + if (rejectResource) { + throw new Error( + "Private resource limit exceeded. Please upgrade your plan." + ); + } + } + let aliasAddress: string | null = null; let releaseAliasLock: (() => Promise) | null = null; if ( @@ -609,6 +643,8 @@ export async function updatePrivateResources( `Created new client resource ${newResource.name} (${newResource.siteResourceId}) for org ${orgId}` ); + await usageService.add(orgId, LimitId.PRIVATE_RESOURCES, 1, trx); + results.push({ newSiteResource: newResource, newSites: allSites, diff --git a/server/lib/blueprints/publicResources.ts b/server/lib/blueprints/publicResources.ts index 1bbe0a4f1..92d6239fa 100644 --- a/server/lib/blueprints/publicResources.ts +++ b/server/lib/blueprints/publicResources.ts @@ -51,6 +51,11 @@ import { build } from "@server/build"; import { encrypt } from "@server/lib/crypto"; import { generateId } from "@server/auth/sessions/app"; import serverConfig from "@server/lib/config"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import next from "next"; +import { LimitId } from "../billing"; +import { usageService } from "../billing/usageService"; export type PublicResourcesResults = { proxyResource: Resource; @@ -1005,6 +1010,33 @@ export async function updatePublicResources( logger.debug(`Updated resource ${existingResource.resourceId}`); } else { // create a brand new resource + + if (build == "saas") { + const usage = await usageService.getUsage( + orgId, + LimitId.PUBLIC_RESOURCES + ); + if (!usage) { + throw new Error( + `Usage data not found for org ${orgId} and limit ${LimitId.PUBLIC_RESOURCES}` + ); + } + const rejectResource = await usageService.checkLimitSet( + orgId, + + LimitId.PUBLIC_RESOURCES, + { + ...usage, + instantaneousValue: (usage.instantaneousValue || 0) + 1 + } // We need to add one to know if we are violating the limit + ); + if (rejectResource) { + throw new Error( + "Public resource limit exceeded. Please upgrade your plan." + ); + } + } + let domain; if ( ["http", "ssh", "rdp", "vnc"].includes(resourceData.mode || "") @@ -1294,6 +1326,8 @@ export async function updatePublicResources( await createTarget(newResource.resourceId, targetData); } + await usageService.add(orgId, LimitId.PUBLIC_RESOURCES, 1, trx); + logger.debug(`Created resource ${newResource.resourceId}`); } diff --git a/server/routers/client/createClient.ts b/server/routers/client/createClient.ts index f64b77b7f..11c7c9ec2 100644 --- a/server/routers/client/createClient.ts +++ b/server/routers/client/createClient.ts @@ -156,7 +156,7 @@ export async function createClient( return next( createHttpError( HttpCode.FORBIDDEN, - "Public resource limit exceeded. Please upgrade your plan." + "Machine client limit exceeded. Please upgrade your plan." ) ); }