From 24998ce0a176acd66667a4863fb1194e412431df Mon Sep 17 00:00:00 2001 From: Owen Date: Fri, 14 Aug 2026 16:33:05 -0400 Subject: [PATCH] Remove advanced resources paywall --- server/lib/billing/tierMatrix.ts | 10 +- server/lib/blueprints/privateResources.ts | 24 -- server/lib/blueprints/publicResources.ts | 16 +- .../routers/billing/featureLifecycle.ts | 17 +- server/private/routers/external.ts | 28 +- server/private/routers/internal.ts | 7 - server/private/routers/ssh/index.ts | 14 - server/routers/external.ts | 26 +- server/routers/internal.ts | 15 +- server/routers/resource/createResource.ts | 17 +- server/routers/role/createRole.ts | 2 +- server/routers/role/updateRole.ts | 2 +- .../siteResource/createSiteResource.ts | 48 +-- .../siteResource/updateSiteResource.ts | 29 +- server/routers/ssh/index.ts | 1 + .../{private => }/routers/ssh/signSshKey.ts | 28 -- .../resources/private/[niceId]/http/page.tsx | 7 +- .../resources/private/[niceId]/ssh/page.tsx | 116 +++--- .../resources/private/create/page.tsx | 177 +++------ .../public/[niceId]/maintenance/page.tsx | 4 +- .../resources/public/[niceId]/rdp/page.tsx | 9 +- .../resources/public/[niceId]/ssh/page.tsx | 9 +- .../resources/public/[niceId]/vnc/page.tsx | 9 +- .../settings/resources/public/create/page.tsx | 62 +-- src/components/CreateRoleForm.tsx | 2 +- src/components/EditRoleForm.tsx | 7 +- src/components/PrivateResourceHttpFields.tsx | 13 +- src/components/PrivateResourceSshFields.tsx | 9 - src/components/RoleForm.tsx | 368 +++++++++--------- src/lib/setClientCookie.ts | 7 - 30 files changed, 368 insertions(+), 715 deletions(-) delete mode 100644 server/private/routers/ssh/index.ts create mode 100644 server/routers/ssh/index.ts rename server/{private => }/routers/ssh/signSshKey.ts (97%) diff --git a/server/lib/billing/tierMatrix.ts b/server/lib/billing/tierMatrix.ts index 7c0b591ca..7e49121dc 100644 --- a/server/lib/billing/tierMatrix.ts +++ b/server/lib/billing/tierMatrix.ts @@ -10,7 +10,7 @@ export enum TierFeature { ActionLogs = "actionLogs", // set the retention period to none on downgrade ConnectionLogs = "connectionLogs", RotateCredentials = "rotateCredentials", - MaintencePage = "maintencePage", // handle downgrade + MaintenancePage = "maintenancePage", // handle downgrade DevicePosture = "devicePosture", TwoFactorEnforcement = "twoFactorEnforcement", // handle downgrade by setting to optional SessionDurationPolicies = "sessionDurationPolicies", // handle downgrade by setting to default duration @@ -25,8 +25,7 @@ export enum TierFeature { WildcardSubdomain = "wildcardSubdomain", NewtAutoUpdate = "newtAutoUpdate", ResourcePolicies = "resourcePolicies", - AdvancedPublicResources = "advancedPublicResources", - AdvancedPrivateResources = "advancedPrivateResources" + RoleBasedSSHControls = "roleBasedSSHControls" } export const tierMatrix: Record = { @@ -39,7 +38,7 @@ export const tierMatrix: Record = { [TierFeature.ActionLogs]: ["tier2", "tier3", "enterprise"], [TierFeature.ConnectionLogs]: ["tier2", "tier3", "enterprise"], [TierFeature.RotateCredentials]: ["tier1", "tier2", "tier3", "enterprise"], - [TierFeature.MaintencePage]: ["tier1", "tier2", "tier3", "enterprise"], + [TierFeature.MaintenancePage]: ["tier1", "tier2", "tier3", "enterprise"], [TierFeature.DevicePosture]: ["tier2", "tier3", "enterprise"], [TierFeature.TwoFactorEnforcement]: [ "tier1", @@ -69,6 +68,5 @@ export const tierMatrix: Record = { [TierFeature.WildcardSubdomain]: ["tier1", "tier2", "tier3", "enterprise"], [TierFeature.NewtAutoUpdate]: ["tier1", "tier2", "tier3", "enterprise"], [TierFeature.ResourcePolicies]: ["tier3", "enterprise"], - [TierFeature.AdvancedPublicResources]: ["tier3", "enterprise"], - [TierFeature.AdvancedPrivateResources]: ["tier3", "enterprise"] + [TierFeature.RoleBasedSSHControls]: ["tier3", "enterprise"] }; diff --git a/server/lib/blueprints/privateResources.ts b/server/lib/blueprints/privateResources.ts index 9cec7b487..cb8f8e8b5 100644 --- a/server/lib/blueprints/privateResources.ts +++ b/server/lib/blueprints/privateResources.ts @@ -128,30 +128,6 @@ export async function updatePrivateResources( for (const [resourceNiceId, resourceData] of Object.entries( config["client-resources"] )) { - if (resourceData.mode === "http") { - const hasHttpFeature = await isLicensedOrSubscribed( - orgId, - tierMatrix.advancedPrivateResources - ); - if (!hasHttpFeature) { - throw new Error( - "HTTP private resources are not included in your current plan. Please upgrade." - ); - } - } - - if (resourceData.mode === "ssh") { - const hasSshFeature = await isLicensedOrSubscribed( - orgId, - tierMatrix.advancedPrivateResources - ); - if (!hasSshFeature) { - throw new Error( - "SSH private resources are not included in your current plan. Please upgrade." - ); - } - } - const [existingResource] = await trx .select() .from(siteResources) diff --git a/server/lib/blueprints/publicResources.ts b/server/lib/blueprints/publicResources.ts index 2f4d9773d..61985a06e 100644 --- a/server/lib/blueprints/publicResources.ts +++ b/server/lib/blueprints/publicResources.ts @@ -262,18 +262,6 @@ export async function updatePublicResources( headers = JSON.stringify(resourceData.headers); } - if (["ssh", "rdp", "vnc"].includes(resourceData.mode || "")) { - const isLicensed = await isLicensedOrSubscribed( - orgId, - tierMatrix.advancedPublicResources - ); - if (!isLicensed) { - throw new Error( - "Your current subscription does not support browser gateway resources. Please upgrade to access this feature." - ); - } - } - if (resourceData.policy) { const isLicensed = await isLicensedOrSubscribed( orgId, @@ -331,7 +319,7 @@ export async function updatePublicResources( const isLicensed = await isLicensedOrSubscribed( orgId, - tierMatrix.maintencePage + tierMatrix.maintenancePage ); if (!isLicensed) { resourceData.maintenance = undefined; @@ -1138,7 +1126,7 @@ export async function updatePublicResources( const isLicensed = await isLicensedOrSubscribed( orgId, - tierMatrix.maintencePage + tierMatrix.maintenancePage ); if (!isLicensed) { resourceData.maintenance = undefined; diff --git a/server/private/routers/billing/featureLifecycle.ts b/server/private/routers/billing/featureLifecycle.ts index 84a7b4f5a..b32d83f7e 100644 --- a/server/private/routers/billing/featureLifecycle.ts +++ b/server/private/routers/billing/featureLifecycle.ts @@ -295,8 +295,8 @@ async function disableFeature( await disableRotateCredentials(orgId); break; - case TierFeature.MaintencePage: - await disableMaintencePage(orgId); + case TierFeature.MaintenancePage: + await disablemaintenancePage(orgId); break; case TierFeature.DevicePosture: @@ -319,10 +319,6 @@ async function disableFeature( await disableAutoProvisioning(orgId); break; - case TierFeature.AdvancedPrivateResources: - await disableAdvancedPrivateResources(orgId); - break; - case TierFeature.FullRbac: await disableFullRbac(orgId); break; @@ -368,13 +364,6 @@ async function disableDeviceApprovals(orgId: string): Promise { logger.info(`Disabled device approvals on all roles for org ${orgId}`); } -async function disableAdvancedPrivateResources(orgId: string): Promise { - // TODO: implement logic to disable advanced private resourcs like ssh and ssh pam - // logger.info( - // `Disabled advanced private resources on all roles and site resources for org ${orgId}` - // ); -} - async function disableFullRbac(orgId: string): Promise { logger.info(`Disabled full RBAC for org ${orgId}`); } @@ -506,7 +495,7 @@ async function disableConnectionLogs(orgId: string): Promise { async function disableRotateCredentials(orgId: string): Promise {} -async function disableMaintencePage(orgId: string): Promise { +async function disablemaintenancePage(orgId: string): Promise { await db .update(resources) .set({ diff --git a/server/private/routers/external.ts b/server/private/routers/external.ts index fab026418..32cb68945 100644 --- a/server/private/routers/external.ts +++ b/server/private/routers/external.ts @@ -20,19 +20,16 @@ import * as orgIdp from "#private/routers/orgIdp"; import * as domain from "#private/routers/domain"; import * as auth from "#private/routers/auth"; import * as license from "#private/routers/license"; -import * as generateLicense from "./generatedLicense"; +import * as generateLicense from "#private/routers/generatedLicense"; import * as logs from "#private/routers/auditLogs"; import * as misc from "#private/routers/misc"; import * as reKey from "#private/routers/re-key"; import * as approval from "#private/routers/approvals"; -import * as ssh from "#private/routers/ssh"; import * as user from "#private/routers/user"; import * as siteProvisioning from "#private/routers/siteProvisioning"; import * as eventStreamingDestination from "#private/routers/eventStreamingDestination"; import * as alertRule from "#private/routers/alertRule"; import * as healthChecks from "#private/routers/healthChecks"; -import * as client from "@server/routers/client"; -import * as resource from "#private/routers/resource"; import * as policy from "#private/routers/policy"; import { @@ -652,17 +649,6 @@ authenticated.put( reKey.reGenerateExitNodeSecret ); -authenticated.post( - "/org/:orgId/ssh/sign-key", - verifyValidLicense, - verifyValidSubscription(tierMatrix.advancedPrivateResources), - verifyOrgAccess, - verifyLimits, - // verifyUserHasAction(ActionsEnum.signSshKey), // this check happens inside of the function now - // logActionAudit(ActionsEnum.signSshKey), // it is handled inside of the function below so we can include more metadata - ssh.signSshKey -); - authenticated.post( "/user/:userId/add-role/:roleId", verifyRoleAccess, @@ -868,18 +854,6 @@ authenticated.get( healthChecks.getBatchedHealthCheckStatusHistory ); -authenticated.get( - "/client/:clientId/verify-associations-cache", - verifyClientAccess, - client.verifyClientAssociationsCache -); - -authenticated.post( - "/client/:clientId/rebuild-associations-cache", - verifyClientAccess, - client.rebuildClientAssociationsCacheRoute -); - authenticated.post( "/org/:orgId/logs/access/attempt", verifyOrgAccess, diff --git a/server/private/routers/internal.ts b/server/private/routers/internal.ts index c45fe36b9..23188dcd7 100644 --- a/server/private/routers/internal.ts +++ b/server/private/routers/internal.ts @@ -17,7 +17,6 @@ import * as orgIdp from "#private/routers/orgIdp"; import * as billing from "#private/routers/billing"; import * as license from "#private/routers/license"; import * as resource from "#private/routers/resource"; -import * as ssh from "#private/routers/ssh"; import * as ws from "@server/routers/ws"; import * as browserTarget from "#private/routers/browserGatewayTarget"; @@ -47,12 +46,6 @@ internalRouter.get(`/license/status`, license.getLicenseStatus); internalRouter.get("/maintenance/info", resource.getMaintenanceInfo); -internalRouter.post( - "/org/:orgId/ssh/sign-key", - verifyUserFromResourceSessionMiddleware, - ssh.signSshKey -); - internalRouter.get( "/ws/round-trip-message/:messageId", verifyUserFromResourceSessionMiddleware, diff --git a/server/private/routers/ssh/index.ts b/server/private/routers/ssh/index.ts deleted file mode 100644 index d2f607f81..000000000 --- a/server/private/routers/ssh/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025-2026 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -export * from "./signSshKey"; \ No newline at end of file diff --git a/server/routers/external.ts b/server/routers/external.ts index e182d2b44..9b7d8e8b0 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -20,6 +20,7 @@ import * as logs from "./auditLogs"; import * as launcher from "./launcher"; import * as newt from "./newt"; import * as olm from "./olm"; +import * as ssh from "./ssh"; import * as serverInfo from "./serverInfo"; import HttpCode from "@server/types/HttpCode"; import { @@ -56,12 +57,13 @@ import rateLimit, { ipKeyGenerator } from "express-rate-limit"; import createHttpError from "http-errors"; import { build } from "@server/build"; import { createStore } from "#dynamic/lib/rateLimitStore"; -import { logActionAudit } from "#dynamic/middlewares"; +import { logActionAudit, verifyValidLicense } from "#dynamic/middlewares"; import { checkRoundTripMessage } from "./ws"; import * as labels from "@server/routers/labels"; import * as aiProvider from "@server/routers/aiProvider"; import * as aiBudget from "@server/routers/aiBudget"; import * as virtualApiKey from "@server/routers/virtualApiKey"; +import { tierMatrix } from "@server/lib/billing/tierMatrix"; // Root routes export const unauthenticated = Router(); @@ -1854,6 +1856,28 @@ authenticated.put( labels.detachLabelFromItem ); +authenticated.post( + "/org/:orgId/ssh/sign-key", + verifyValidLicense, + verifyOrgAccess, + verifyLimits, + // verifyUserHasAction(ActionsEnum.signSshKey), // this check happens inside of the function now + // logActionAudit(ActionsEnum.signSshKey), // it is handled inside of the function below so we can include more metadata + ssh.signSshKey +); + +authenticated.get( + "/client/:clientId/verify-associations-cache", + verifyClientAccess, + client.verifyClientAssociationsCache +); + +authenticated.post( + "/client/:clientId/rebuild-associations-cache", + verifyClientAccess, + client.rebuildClientAssociationsCacheRoute +); + // Auth routes export const authRouter = Router(); unauthenticated.use("/auth", authRouter); diff --git a/server/routers/internal.ts b/server/routers/internal.ts index b06015a5f..27414c47b 100644 --- a/server/routers/internal.ts +++ b/server/routers/internal.ts @@ -1,15 +1,17 @@ import { Router } from "express"; import * as gerbil from "@server/routers/gerbil"; import * as traefik from "@server/routers/traefik"; -import * as resource from "./resource"; -import * as badger from "./badger"; +import * as resource from "@server/routers/resource"; +import * as badger from "@server/routers/badger"; import * as auth from "@server/routers/auth"; import * as supporterKey from "@server/routers/supporterKey"; import * as idp from "@server/routers/idp"; +import * as ssh from "@server/routers/ssh"; import HttpCode from "@server/types/HttpCode"; import { verifyResourceAccess, - verifySessionUserMiddleware + verifySessionUserMiddleware, + verifyUserFromResourceSessionMiddleware } from "@server/middlewares"; // Root routes @@ -42,6 +44,12 @@ internalRouter.get("/idp", idp.listIdps); internalRouter.get("/idp/:idpId", idp.getIdp); +internalRouter.post( + "/org/:orgId/ssh/sign-key", + verifyUserFromResourceSessionMiddleware, + ssh.signSshKey +); + // Gerbil routes const gerbilRouter = Router(); internalRouter.use("/gerbil", gerbilRouter); @@ -63,4 +71,3 @@ internalRouter.use("/badger", badgerRouter); badgerRouter.post("/verify-session", badger.verifyResourceSession); badgerRouter.post("/exchange-session", badger.exchangeSession); - diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index c0c7a7d5d..094e092b3 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -31,7 +31,7 @@ import { } from "@server/lib/domainUtils"; import { isSubscribed } from "#dynamic/lib/isSubscribed"; import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; -import { TierFeature, tierMatrix } from "@server/lib/billing/tierMatrix"; +import { tierMatrix } from "@server/lib/billing/tierMatrix"; import { getUniqueResourceName, getUniqueResourcePolicyName @@ -454,21 +454,6 @@ async function createHttpResource( } } - if ( - ["ssh", "rdp", "vnc"].includes(effectiveMode) && - !isLicensedOrSubscribed( - orgId!, - tierMatrix[TierFeature.AdvancedPublicResources] - ) - ) { - return next( - createHttpError( - HttpCode.BAD_REQUEST, - "Your current subscription does not support browser gateway resources. Please upgrade to access this feature." - ) - ); - } - // Validate domain and construct full domain const domainResult = await validateAndConstructDomain( domainId, diff --git a/server/routers/role/createRole.ts b/server/routers/role/createRole.ts index 6d1ecb503..1cce7a5ae 100644 --- a/server/routers/role/createRole.ts +++ b/server/routers/role/createRole.ts @@ -135,7 +135,7 @@ export async function createRole( const isLicensedSshPam = await isLicensedOrSubscribed( orgId, - tierMatrix.advancedPrivateResources + tierMatrix.roleBasedSSHControls ); const roleInsertValues: Record = { name: roleData.name, diff --git a/server/routers/role/updateRole.ts b/server/routers/role/updateRole.ts index aa01899db..2c1dcc887 100644 --- a/server/routers/role/updateRole.ts +++ b/server/routers/role/updateRole.ts @@ -144,7 +144,7 @@ export async function updateRole( const isLicensedSshPam = await isLicensedOrSubscribed( orgId, - tierMatrix.advancedPrivateResources + tierMatrix.roleBasedSSHControls ); if (!isLicensedSshPam) { delete updateData.sshSudoMode; diff --git a/server/routers/siteResource/createSiteResource.ts b/server/routers/siteResource/createSiteResource.ts index f3794d35a..54a1bc004 100644 --- a/server/routers/siteResource/createSiteResource.ts +++ b/server/routers/siteResource/createSiteResource.ts @@ -10,8 +10,7 @@ import { SiteResource, siteResources, sites, - userSiteResources, - primaryDb + userSiteResources } from "@server/db"; import { getUniqueSiteResourceName } from "@server/db/names"; import { @@ -19,8 +18,6 @@ import { isIpInCidr, portRangeStringSchema } from "@server/lib/ip"; -import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; -import { TierFeature, tierMatrix } from "@server/lib/billing/tierMatrix"; import { rebuildClientAssociationsFromSiteResource, isOrgRebuildRateLimited @@ -408,21 +405,6 @@ export async function createSiteResource( } } - if (mode == "http") { - const hasHttpFeature = await isLicensedOrSubscribed( - orgId, - tierMatrix[TierFeature.AdvancedPrivateResources] - ); - if (!hasHttpFeature) { - return next( - createHttpError( - HttpCode.FORBIDDEN, - "HTTP private resources are not included in your current plan. Please upgrade." - ) - ); - } - } - // Verify the site exists and belongs to the org const sitesToAssign = await db .select() @@ -557,20 +539,6 @@ export async function createSiteResource( } } - const isLicensedSshPam = await isLicensedOrSubscribed( - orgId, - tierMatrix.advancedPrivateResources - ); - - if (mode == "ssh" && !isLicensedSshPam) { - return next( - createHttpError( - HttpCode.FORBIDDEN, - "SSH private resources are not included in your current plan. Please upgrade." - ) - ); - } - let updatedNiceId = niceId; if (!niceId) { updatedNiceId = await getUniqueSiteResourceName(orgId); @@ -646,13 +614,13 @@ export async function createSiteResource( fullDomain, requiresExitNodeConnection: mode === "inference" // in the future we might want to have different modes that do this }; - if (isLicensedSshPam) { - if (authDaemonPort !== undefined) - insertValues.authDaemonPort = authDaemonPort; - if (authDaemonMode !== undefined) - insertValues.authDaemonMode = authDaemonMode; - if (pamMode !== undefined) insertValues.pamMode = pamMode; - } + + if (authDaemonPort !== undefined) + insertValues.authDaemonPort = authDaemonPort; + if (authDaemonMode !== undefined) + insertValues.authDaemonMode = authDaemonMode; + if (pamMode !== undefined) insertValues.pamMode = pamMode; + [newSiteResource] = await trx .insert(siteResources) .values(insertValues) diff --git a/server/routers/siteResource/updateSiteResource.ts b/server/routers/siteResource/updateSiteResource.ts index 2e57b13a7..f0ee514a7 100644 --- a/server/routers/siteResource/updateSiteResource.ts +++ b/server/routers/siteResource/updateSiteResource.ts @@ -10,8 +10,6 @@ import { sites, userSiteResources } from "@server/db"; -import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; -import { TierFeature, tierMatrix } from "@server/lib/billing/tierMatrix"; import { validateAndConstructDomain } from "@server/lib/domainUtils"; import response from "@server/lib/response"; import { eq, and, ne, inArray } from "drizzle-orm"; @@ -362,26 +360,6 @@ export async function updateSiteResource( ); } - if (mode == "http") { - const hasHttpFeature = await isLicensedOrSubscribed( - existingSiteResource.orgId, - tierMatrix[TierFeature.AdvancedPrivateResources] - ); - if (!hasHttpFeature) { - return next( - createHttpError( - HttpCode.FORBIDDEN, - "HTTP private resources are not included in your current plan. Please upgrade." - ) - ); - } - } - - const isLicensedSshPam = await isLicensedOrSubscribed( - existingSiteResource.orgId, - tierMatrix.advancedPrivateResources - ); - const [org] = await db .select() .from(orgs) @@ -541,10 +519,9 @@ export async function updateSiteResource( await db.transaction(async (trx) => { // Update the site resource const sshPamSet = - isLicensedSshPam && - (authDaemonPort !== undefined || - authDaemonMode !== undefined || - pamMode !== undefined) + authDaemonPort !== undefined || + authDaemonMode !== undefined || + pamMode !== undefined ? { ...(authDaemonPort !== undefined && { authDaemonPort diff --git a/server/routers/ssh/index.ts b/server/routers/ssh/index.ts new file mode 100644 index 000000000..4cfcb6df3 --- /dev/null +++ b/server/routers/ssh/index.ts @@ -0,0 +1 @@ +export * from "./signSshKey"; \ No newline at end of file diff --git a/server/private/routers/ssh/signSshKey.ts b/server/routers/ssh/signSshKey.ts similarity index 97% rename from server/private/routers/ssh/signSshKey.ts rename to server/routers/ssh/signSshKey.ts index ae74a07a0..dfd73fd13 100644 --- a/server/private/routers/ssh/signSshKey.ts +++ b/server/routers/ssh/signSshKey.ts @@ -1,16 +1,3 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025-2026 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - import { Request, Response, NextFunction } from "express"; import { randomInt } from "crypto"; import { z } from "zod"; @@ -35,8 +22,6 @@ import { SiteResource } from "@server/db"; import { logAccessAudit } from "#private/lib/logAccessAudit"; -import { isLicensedOrSubscribed } from "#private/lib/isLicencedOrSubscribed"; -import { tierMatrix } from "@server/lib/billing/tierMatrix"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; @@ -163,19 +148,6 @@ export async function signSshKey( ); } - const isLicensed = await isLicensedOrSubscribed( - orgId, - tierMatrix.advancedPrivateResources - ); - if (!isLicensed) { - return next( - createHttpError( - HttpCode.FORBIDDEN, - "SSH key signing requires a paid plan" - ) - ); - } - // Get and decrypt the org's CA keys const caKeys = await getOrgCAKeys( orgId, diff --git a/src/app/[orgId]/settings/resources/private/[niceId]/http/page.tsx b/src/app/[orgId]/settings/resources/private/[niceId]/http/page.tsx index c1263c162..c764b0446 100644 --- a/src/app/[orgId]/settings/resources/private/[niceId]/http/page.tsx +++ b/src/app/[orgId]/settings/resources/private/[niceId]/http/page.tsx @@ -35,10 +35,6 @@ import { buildSelectedSitesForResource } from "@app/lib/privateResourceUtils"; export default function PrivateResourceHttpPage() { const t = useTranslations(); const { save, siteResource } = useSaveSiteResource(); - const { isPaidUser } = usePaidStatus(); - const httpSectionDisabled = !isPaidUser( - tierMatrix.advancedPrivateResources - ); const [selectedSites, setSelectedSites] = useState(() => buildSelectedSitesForResource(siteResource) ); @@ -120,7 +116,7 @@ export default function PrivateResourceHttpPage() { )} orgId={siteResource.orgId} watch={asAnyWatch(form.watch)} - disabled={httpSectionDisabled} + disabled={false} siteResourceId={siteResource.id} /> @@ -135,7 +131,6 @@ export default function PrivateResourceHttpPage() { type="submit" form="private-resource-http-form" loading={saveLoading} - disabled={httpSectionDisabled} > {t("saveSettings")} diff --git a/src/app/[orgId]/settings/resources/private/[niceId]/ssh/page.tsx b/src/app/[orgId]/settings/resources/private/[niceId]/ssh/page.tsx index 9a739a073..ba60e41e6 100644 --- a/src/app/[orgId]/settings/resources/private/[niceId]/ssh/page.tsx +++ b/src/app/[orgId]/settings/resources/private/[niceId]/ssh/page.tsx @@ -12,16 +12,13 @@ import { SettingsFormGrid } from "@app/components/Settings"; import { SshServerSettingsFields } from "@app/components/SshServerSettingsFields"; -import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert"; import { Button } from "@app/components/ui/button"; import { Form } from "@app/components/ui/form"; -import { usePaidStatus } from "@app/hooks/usePaidStatus"; import { createSshFormSchema, inferSshPamMode } from "@app/lib/privateResourceForm"; import { zodResolver } from "@hookform/resolvers/zod"; -import { tierMatrix } from "@server/lib/billing/tierMatrix"; import { useTranslations } from "next-intl"; import { useActionState, useMemo, useState } from "react"; import { useForm } from "react-hook-form"; @@ -39,8 +36,6 @@ import { buildSelectedSitesForResource } from "@app/lib/privateResourceUtils"; export default function PrivateResourceSshPage() { const t = useTranslations(); const { save, siteResource } = useSaveSiteResource(); - const { isPaidUser } = usePaidStatus(); - const sshSectionDisabled = !isPaidUser(tierMatrix.advancedPrivateResources); const isNative = siteResource.authDaemonMode === "native"; const [sshServerMode] = useState<"standard" | "native">( isNative ? "native" : "standard" @@ -150,7 +145,6 @@ export default function PrivateResourceSshPage() { return ( - @@ -161,68 +155,56 @@ export default function PrivateResourceSshPage() { -
-
- - - - - form.setValue( - "authDaemonPort", - value, - { shouldValidate: true } - ) - } - authDaemonPortError={ - form.formState.errors.authDaemonPort - ?.message - } - sshServerMode={sshServerMode} - serverModeDisplay="badge" - /> - - - - + + + + + + form.setValue("authDaemonPort", value, { + shouldValidate: true + }) + } + authDaemonPortError={ + form.formState.errors.authDaemonPort + ?.message + } + sshServerMode={sshServerMode} + serverModeDisplay="badge" + /> + + + + - - - - -
- -
+ +
+ +
+
+
); diff --git a/src/app/[orgId]/settings/resources/private/create/page.tsx b/src/app/[orgId]/settings/resources/private/create/page.tsx index 96e8f84d8..717464283 100644 --- a/src/app/[orgId]/settings/resources/private/create/page.tsx +++ b/src/app/[orgId]/settings/resources/private/create/page.tsx @@ -16,7 +16,6 @@ import { type DescribedSelectOption } from "@app/components/DescribedSelect"; import DomainPicker from "@app/components/DomainPicker"; -import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert"; import { Button } from "@app/components/ui/button"; import { Form, @@ -30,7 +29,6 @@ import { import { Input } from "@app/components/ui/input"; import type { Selectedsite } from "@app/components/site-selector"; import { useEnvContext } from "@app/hooks/useEnvContext"; -import { usePaidStatus } from "@app/hooks/usePaidStatus"; import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; import { @@ -77,12 +75,6 @@ export default function CreatePrivateResourcePage() { const { env } = useEnvContext(); const api = createApiClient({ env }); const orgId = params.orgId as string; - const disableEnterpriseFeatures = env.flags.disableEnterpriseFeatures; - const { isPaidUser } = usePaidStatus(); - const httpSectionDisabled = !isPaidUser( - tierMatrix.advancedPrivateResources - ); - const sshSectionDisabled = !isPaidUser(tierMatrix.advancedPrivateResources); const [isSubmitting, startTransition] = useTransition(); const siteIdParam = searchParams.get("siteId"); @@ -158,20 +150,16 @@ export default function CreatePrivateResourcePage() { title: t("createInternalResourceDialogModeCidr"), description: t("privateResourceTypeCidrDescription") }, - ...(!disableEnterpriseFeatures - ? [ - { - value: "http" as const, - title: t("createInternalResourceDialogModeHttp"), - description: t("privateResourceTypeHttpDescription") - }, - { - value: "ssh" as const, - title: t("createInternalResourceDialogModeSsh"), - description: t("privateResourceTypeSshDescription") - } - ] - : []), + { + value: "http" as const, + title: t("createInternalResourceDialogModeHttp"), + description: t("privateResourceTypeHttpDescription") + }, + { + value: "ssh" as const, + title: t("createInternalResourceDialogModeSsh"), + description: t("privateResourceTypeSshDescription") + }, { value: "inference" as const, title: t("createInternalResourceDialogModeInference"), @@ -179,11 +167,6 @@ export default function CreatePrivateResourcePage() { } ]; - const submitDisabled = - isSubmitting || - (mode === "http" && httpSectionDisabled) || - (mode === "ssh" && sshSectionDisabled); - function onSubmit(values: FormValues) { startTransition(async () => { try { @@ -467,10 +450,7 @@ export default function CreatePrivateResourcePage() { )} watch={asAnyWatch(form.watch)} labelPrefix="create" - disabled={ - mode === "ssh" && - sshSectionDisabled - } + disabled={false} /> )} @@ -584,9 +564,6 @@ export default function CreatePrivateResourcePage() { {/* HTTP configuration */} {mode === "http" && ( - {t("httpSettings")} @@ -597,62 +574,44 @@ export default function CreatePrivateResourcePage() { )} -
- - - - - - - - - - - - -
+ + + + + + + + + + + + +
)} {/* SSH server */} {mode === "ssh" && ( - {t("sshSettings")} @@ -661,37 +620,23 @@ export default function CreatePrivateResourcePage() { {t("sshServerDescription")} -
- - - - - -
+ + + + +
)} @@ -776,7 +721,7 @@ export default function CreatePrivateResourcePage() {