mirror of
https://github.com/fosrl/pangolin.git
synced 2026-06-05 23:28:44 +00:00
Properly paywall the new resource types
This commit is contained in:
@@ -10,11 +10,14 @@ import {
|
||||
SettingsSectionTitle
|
||||
} from "@app/components/Settings";
|
||||
import { BrowserGatewayTargetForm } from "@app/components/BrowserGatewayTargetForm";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import { type Selectedsite } from "@app/components/site-selector";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { useResourceContext } from "@app/hooks/useResourceContext";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
import { tierMatrix, TierFeature } from "@server/lib/billing/tierMatrix";
|
||||
import { createApiClient } from "@app/lib/api";
|
||||
import { formatAxiosError } from "@app/lib/api/formatAxiosError";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
@@ -48,13 +51,21 @@ export default function SshSettingsPage(props: {
|
||||
}) {
|
||||
const params = use(props.params);
|
||||
const { resource, updateResource } = useResourceContext();
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
const disabled = !isPaidUser(
|
||||
tierMatrix[TierFeature.AdvancedPublicResources]
|
||||
);
|
||||
|
||||
return (
|
||||
<SettingsContainer>
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix[TierFeature.AdvancedPublicResources]}
|
||||
/>
|
||||
<SshServerForm
|
||||
orgId={params.orgId}
|
||||
resource={resource}
|
||||
updateResource={updateResource}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</SettingsContainer>
|
||||
);
|
||||
@@ -63,11 +74,13 @@ export default function SshSettingsPage(props: {
|
||||
function SshServerForm({
|
||||
orgId,
|
||||
resource,
|
||||
updateResource
|
||||
updateResource,
|
||||
disabled
|
||||
}: {
|
||||
orgId: string;
|
||||
resource: GetResourceResponse;
|
||||
updateResource: ResourceContextType["updateResource"];
|
||||
disabled: boolean;
|
||||
}) {
|
||||
const t = useTranslations();
|
||||
const api = createApiClient(useEnvContext());
|
||||
@@ -220,31 +233,36 @@ function SshServerForm({
|
||||
{t("rdpServerDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<BrowserGatewayTargetForm
|
||||
orgId={orgId}
|
||||
multiSite={true}
|
||||
selectedSites={selectedSites}
|
||||
onSitesChange={setSelectedSites}
|
||||
destination={bgDestination}
|
||||
destinationPort={bgDestinationPort}
|
||||
onDestinationChange={setBgDestination}
|
||||
onDestinationPortChange={setBgDestinationPort}
|
||||
learnMoreHref="https://docs.pangolin.net/manage/resources/public/rdp"
|
||||
defaultPort={3389}
|
||||
/>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
<form action={formAction} className="flex justify-end mt-4">
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
type="submit"
|
||||
>
|
||||
{t("saveSettings")}
|
||||
</Button>
|
||||
</form>
|
||||
<fieldset
|
||||
disabled={disabled}
|
||||
className={disabled ? "opacity-50 pointer-events-none" : ""}
|
||||
>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<BrowserGatewayTargetForm
|
||||
orgId={orgId}
|
||||
multiSite={true}
|
||||
selectedSites={selectedSites}
|
||||
onSitesChange={setSelectedSites}
|
||||
destination={bgDestination}
|
||||
destinationPort={bgDestinationPort}
|
||||
onDestinationChange={setBgDestination}
|
||||
onDestinationPortChange={setBgDestinationPort}
|
||||
learnMoreHref="https://docs.pangolin.net/manage/resources/public/rdp"
|
||||
defaultPort={3389}
|
||||
/>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
<form action={formAction} className="flex justify-end mt-4">
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
type="submit"
|
||||
>
|
||||
{t("saveSettings")}
|
||||
</Button>
|
||||
</form>
|
||||
</fieldset>
|
||||
</SettingsSection>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,10 +11,13 @@ import {
|
||||
} from "@app/components/Settings";
|
||||
import { StrategySelect, StrategyOption } from "@app/components/StrategySelect";
|
||||
import { BrowserGatewayTargetForm } from "@app/components/BrowserGatewayTargetForm";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import {
|
||||
SitesSelector,
|
||||
type Selectedsite
|
||||
} from "@app/components/site-selector";
|
||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
import { tierMatrix, TierFeature } from "@server/lib/billing/tierMatrix";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { Input } from "@app/components/ui/input";
|
||||
import {
|
||||
@@ -68,13 +71,21 @@ export default function SshSettingsPage(props: {
|
||||
}) {
|
||||
const params = use(props.params);
|
||||
const { resource, updateResource } = useResourceContext();
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
const disabled = !isPaidUser(
|
||||
tierMatrix[TierFeature.AdvancedPublicResources]
|
||||
);
|
||||
|
||||
return (
|
||||
<SettingsContainer>
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix[TierFeature.AdvancedPublicResources]}
|
||||
/>
|
||||
<SshServerForm
|
||||
orgId={params.orgId}
|
||||
resource={resource}
|
||||
updateResource={updateResource}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</SettingsContainer>
|
||||
);
|
||||
@@ -83,11 +94,13 @@ export default function SshSettingsPage(props: {
|
||||
function SshServerForm({
|
||||
orgId,
|
||||
resource,
|
||||
updateResource
|
||||
updateResource,
|
||||
disabled
|
||||
}: {
|
||||
orgId: string;
|
||||
resource: GetResourceResponse;
|
||||
updateResource: ResourceContextType["updateResource"];
|
||||
disabled: boolean;
|
||||
}) {
|
||||
const t = useTranslations();
|
||||
const api = createApiClient(useEnvContext());
|
||||
@@ -366,6 +379,10 @@ function SshServerForm({
|
||||
{t("sshServerDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<fieldset
|
||||
disabled={disabled}
|
||||
className={disabled ? "opacity-50 pointer-events-none" : ""}
|
||||
>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<div className="space-y-3">
|
||||
@@ -520,6 +537,7 @@ function SshServerForm({
|
||||
{t("saveSettings")}
|
||||
</Button>
|
||||
</form>
|
||||
</fieldset>
|
||||
</SettingsSection>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,11 +10,14 @@ import {
|
||||
SettingsSectionTitle
|
||||
} from "@app/components/Settings";
|
||||
import { BrowserGatewayTargetForm } from "@app/components/BrowserGatewayTargetForm";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import { type Selectedsite } from "@app/components/site-selector";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { useResourceContext } from "@app/hooks/useResourceContext";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
import { tierMatrix, TierFeature } from "@server/lib/billing/tierMatrix";
|
||||
import { createApiClient } from "@app/lib/api";
|
||||
import { formatAxiosError } from "@app/lib/api/formatAxiosError";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
@@ -46,13 +49,21 @@ export default function SshSettingsPage(props: {
|
||||
}) {
|
||||
const params = use(props.params);
|
||||
const { resource, updateResource } = useResourceContext();
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
const disabled = !isPaidUser(
|
||||
tierMatrix[TierFeature.AdvancedPublicResources]
|
||||
);
|
||||
|
||||
return (
|
||||
<SettingsContainer>
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix[TierFeature.AdvancedPublicResources]}
|
||||
/>
|
||||
<SshServerForm
|
||||
orgId={params.orgId}
|
||||
resource={resource}
|
||||
updateResource={updateResource}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</SettingsContainer>
|
||||
);
|
||||
@@ -61,11 +72,13 @@ export default function SshSettingsPage(props: {
|
||||
function SshServerForm({
|
||||
orgId,
|
||||
resource,
|
||||
updateResource
|
||||
updateResource,
|
||||
disabled
|
||||
}: {
|
||||
orgId: string;
|
||||
resource: GetResourceResponse;
|
||||
updateResource: ResourceContextType["updateResource"];
|
||||
disabled: boolean;
|
||||
}) {
|
||||
const t = useTranslations();
|
||||
const api = createApiClient(useEnvContext());
|
||||
@@ -218,31 +231,36 @@ function SshServerForm({
|
||||
{t("vncServerDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<BrowserGatewayTargetForm
|
||||
orgId={orgId}
|
||||
multiSite={true}
|
||||
selectedSites={selectedSites}
|
||||
onSitesChange={setSelectedSites}
|
||||
destination={bgDestination}
|
||||
destinationPort={bgDestinationPort}
|
||||
onDestinationChange={setBgDestination}
|
||||
onDestinationPortChange={setBgDestinationPort}
|
||||
learnMoreHref="https://docs.pangolin.net/manage/resources/public/vnc"
|
||||
defaultPort={5900}
|
||||
/>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
<form action={formAction} className="flex justify-end mt-4">
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
type="submit"
|
||||
>
|
||||
{t("saveSettings")}
|
||||
</Button>
|
||||
</form>
|
||||
<fieldset
|
||||
disabled={disabled}
|
||||
className={disabled ? "opacity-50 pointer-events-none" : ""}
|
||||
>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<BrowserGatewayTargetForm
|
||||
orgId={orgId}
|
||||
multiSite={true}
|
||||
selectedSites={selectedSites}
|
||||
onSitesChange={setSelectedSites}
|
||||
destination={bgDestination}
|
||||
destinationPort={bgDestinationPort}
|
||||
onDestinationChange={setBgDestination}
|
||||
onDestinationPortChange={setBgDestinationPort}
|
||||
learnMoreHref="https://docs.pangolin.net/manage/resources/public/vnc"
|
||||
defaultPort={5900}
|
||||
/>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
<form action={formAction} className="flex justify-end mt-4">
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
type="submit"
|
||||
>
|
||||
{t("saveSettings")}
|
||||
</Button>
|
||||
</form>
|
||||
</fieldset>
|
||||
</SettingsSection>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,10 @@ import {
|
||||
} from "@app/components/ui/tooltip";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import { tierMatrix, TierFeature } from "@server/lib/billing/tierMatrix";
|
||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||
import { DockerManager, DockerState } from "@app/lib/docker";
|
||||
import { orgQueries } from "@app/lib/queries";
|
||||
@@ -226,6 +229,8 @@ export default function Page() {
|
||||
orgQueries.sites({ orgId: orgId as string })
|
||||
);
|
||||
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
|
||||
const [remoteExitNodes, setRemoteExitNodes] = useState<
|
||||
ListRemoteExitNodesResponse["remoteExitNodes"]
|
||||
>([]);
|
||||
@@ -238,6 +243,14 @@ export default function Page() {
|
||||
// Resource type state
|
||||
const [resourceType, setResourceType] = useState<NewResourceType>("http");
|
||||
|
||||
const isBrowserGatewayType =
|
||||
resourceType === "ssh" ||
|
||||
resourceType === "rdp" ||
|
||||
resourceType === "vnc";
|
||||
const browserGatewayDisabled =
|
||||
isBrowserGatewayType &&
|
||||
!isPaidUser(tierMatrix[TierFeature.AdvancedPublicResources]);
|
||||
|
||||
// Target management state (managed by ProxyResourceTargetsForm; mirrored here for onSubmit)
|
||||
const [targets, setTargets] = useState<LocalTarget[]>([]);
|
||||
|
||||
@@ -870,6 +883,14 @@ export default function Page() {
|
||||
{/* SSH Server Section */}
|
||||
{resourceType === "ssh" && (
|
||||
<SettingsSection>
|
||||
<PaidFeaturesAlert
|
||||
tiers={
|
||||
tierMatrix[
|
||||
TierFeature
|
||||
.AdvancedPublicResources
|
||||
]
|
||||
}
|
||||
/>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
{t("sshServer")}
|
||||
@@ -878,6 +899,14 @@ export default function Page() {
|
||||
{t("sshServerDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<fieldset
|
||||
disabled={browserGatewayDisabled}
|
||||
className={
|
||||
browserGatewayDisabled
|
||||
? "opacity-50 pointer-events-none"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
{/* Mode */}
|
||||
@@ -1098,12 +1127,21 @@ export default function Page() {
|
||||
</div>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
</fieldset>
|
||||
</SettingsSection>
|
||||
)}
|
||||
|
||||
{/* RDP Server Section */}
|
||||
{resourceType === "rdp" && (
|
||||
<SettingsSection>
|
||||
<PaidFeaturesAlert
|
||||
tiers={
|
||||
tierMatrix[
|
||||
TierFeature
|
||||
.AdvancedPublicResources
|
||||
]
|
||||
}
|
||||
/>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
{t("rdpServer")}
|
||||
@@ -1112,6 +1150,14 @@ export default function Page() {
|
||||
{t("rdpServerDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<fieldset
|
||||
disabled={browserGatewayDisabled}
|
||||
className={
|
||||
browserGatewayDisabled
|
||||
? "opacity-50 pointer-events-none"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<BrowserGatewayTargetForm
|
||||
@@ -1136,12 +1182,21 @@ export default function Page() {
|
||||
/>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
</fieldset>
|
||||
</SettingsSection>
|
||||
)}
|
||||
|
||||
{/* VNC Server Section */}
|
||||
{resourceType === "vnc" && (
|
||||
<SettingsSection>
|
||||
<PaidFeaturesAlert
|
||||
tiers={
|
||||
tierMatrix[
|
||||
TierFeature
|
||||
.AdvancedPublicResources
|
||||
]
|
||||
}
|
||||
/>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
{t("vncServer")}
|
||||
@@ -1150,6 +1205,14 @@ export default function Page() {
|
||||
{t("vncServerDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<fieldset
|
||||
disabled={browserGatewayDisabled}
|
||||
className={
|
||||
browserGatewayDisabled
|
||||
? "opacity-50 pointer-events-none"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<BrowserGatewayTargetForm
|
||||
@@ -1174,6 +1237,7 @@ export default function Page() {
|
||||
/>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
</fieldset>
|
||||
</SettingsSection>
|
||||
)}
|
||||
|
||||
@@ -1225,7 +1289,7 @@ export default function Page() {
|
||||
}
|
||||
}}
|
||||
loading={createLoading}
|
||||
disabled={!areAllTargetsValid()}
|
||||
disabled={!areAllTargetsValid() || browserGatewayDisabled}
|
||||
>
|
||||
{t("resourceCreate")}
|
||||
</Button>
|
||||
|
||||
@@ -16,10 +16,7 @@ import { useOrgContext } from "@app/hooks/useOrgContext";
|
||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||
import type {
|
||||
CreateRoleBody,
|
||||
CreateRoleResponse
|
||||
} from "@server/routers/role";
|
||||
import type { CreateRoleBody, CreateRoleResponse } from "@server/routers/role";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTransition } from "react";
|
||||
@@ -50,7 +47,7 @@ export default function CreateRoleForm({
|
||||
requireDeviceApproval: values.requireDeviceApproval,
|
||||
allowSsh: values.allowSsh
|
||||
};
|
||||
if (isPaidUser(tierMatrix.sshPam)) {
|
||||
if (isPaidUser(tierMatrix.advancedPrivateResources)) {
|
||||
payload.sshSudoMode = values.sshSudoMode;
|
||||
payload.sshCreateHomeDir = values.sshCreateHomeDir;
|
||||
payload.sshSudoCommands =
|
||||
@@ -69,10 +66,9 @@ export default function CreateRoleForm({
|
||||
}
|
||||
}
|
||||
const res = await api
|
||||
.put<AxiosResponse<CreateRoleResponse>>(
|
||||
`/org/${org?.org.orgId}/role`,
|
||||
payload
|
||||
)
|
||||
.put<
|
||||
AxiosResponse<CreateRoleResponse>
|
||||
>(`/org/${org?.org.orgId}/role`, payload)
|
||||
.catch((e) => {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
|
||||
@@ -16,10 +16,7 @@ import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||
import type { Role } from "@server/db";
|
||||
import type {
|
||||
UpdateRoleBody,
|
||||
UpdateRoleResponse
|
||||
} from "@server/routers/role";
|
||||
import type { UpdateRoleBody, UpdateRoleResponse } from "@server/routers/role";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTransition } from "react";
|
||||
@@ -53,7 +50,7 @@ export default function EditRoleForm({
|
||||
payload.name = values.name;
|
||||
payload.description = values.description || undefined;
|
||||
}
|
||||
if (isPaidUser(tierMatrix.sshPam)) {
|
||||
if (isPaidUser(tierMatrix.advancedPrivateResources)) {
|
||||
payload.sshSudoMode = values.sshSudoMode;
|
||||
payload.sshCreateHomeDir = values.sshCreateHomeDir;
|
||||
payload.sshSudoCommands =
|
||||
@@ -72,10 +69,9 @@ export default function EditRoleForm({
|
||||
}
|
||||
}
|
||||
const res = await api
|
||||
.post<AxiosResponse<UpdateRoleResponse>>(
|
||||
`/role/${role.roleId}`,
|
||||
payload
|
||||
)
|
||||
.post<
|
||||
AxiosResponse<UpdateRoleResponse>
|
||||
>(`/role/${role.roleId}`, payload)
|
||||
.catch((e) => {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
|
||||
@@ -224,8 +224,10 @@ export function PrivateResourceForm({
|
||||
const { env } = useEnvContext();
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
const disableEnterpriseFeatures = env.flags.disableEnterpriseFeatures;
|
||||
const sshSectionDisabled = !isPaidUser(tierMatrix.sshPam);
|
||||
const httpSectionDisabled = !isPaidUser(tierMatrix.httpPrivateResources);
|
||||
const sshSectionDisabled = !isPaidUser(tierMatrix.advancedPrivateResources);
|
||||
const httpSectionDisabled = !isPaidUser(
|
||||
tierMatrix.advancedPrivateResources
|
||||
);
|
||||
|
||||
const nameRequiredKey =
|
||||
variant === "create"
|
||||
@@ -594,6 +596,7 @@ export function PrivateResourceForm({
|
||||
const httpConfigDomainId = form.watch("httpConfigDomainId");
|
||||
const httpConfigFullDomain = form.watch("httpConfigFullDomain");
|
||||
const isHttpMode = mode === "http";
|
||||
const isSshMode = mode === "ssh";
|
||||
const authDaemonMode = form.watch("authDaemonMode") ?? "site";
|
||||
const pamMode = form.watch("pamMode") ?? "passthrough";
|
||||
const isNative = sshServerMode === "native";
|
||||
@@ -739,8 +742,17 @@ export function PrivateResourceForm({
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
onSubmitDisabledChange?.(isHttpMode && httpSectionDisabled);
|
||||
}, [isHttpMode, httpSectionDisabled, onSubmitDisabledChange]);
|
||||
onSubmitDisabledChange?.(
|
||||
(isHttpMode && httpSectionDisabled) ||
|
||||
(isSshMode && sshSectionDisabled)
|
||||
);
|
||||
}, [
|
||||
isHttpMode,
|
||||
httpSectionDisabled,
|
||||
isSshMode,
|
||||
sshSectionDisabled,
|
||||
onSubmitDisabledChange
|
||||
]);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
@@ -1129,8 +1141,10 @@ export function PrivateResourceForm({
|
||||
""
|
||||
}
|
||||
disabled={
|
||||
isHttpMode &&
|
||||
httpSectionDisabled
|
||||
(isHttpMode &&
|
||||
httpSectionDisabled) ||
|
||||
(isSshMode &&
|
||||
sshSectionDisabled)
|
||||
}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
@@ -1169,6 +1183,10 @@ export function PrivateResourceForm({
|
||||
field.value ??
|
||||
""
|
||||
}
|
||||
disabled={
|
||||
isSshMode &&
|
||||
sshSectionDisabled
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -1202,7 +1220,10 @@ export function PrivateResourceForm({
|
||||
""
|
||||
}
|
||||
disabled={
|
||||
httpSectionDisabled
|
||||
(isHttpMode &&
|
||||
httpSectionDisabled) ||
|
||||
(isSshMode &&
|
||||
sshSectionDisabled)
|
||||
}
|
||||
onChange={(e) => {
|
||||
const raw =
|
||||
@@ -1237,9 +1258,9 @@ export function PrivateResourceForm({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isHttpMode && (
|
||||
{(isHttpMode || isSshMode) && (
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.httpPrivateResources}
|
||||
tiers={tierMatrix.advancedPrivateResources}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1773,7 +1794,9 @@ export function PrivateResourceForm({
|
||||
{/* SSH Access tab (ssh mode only) */}
|
||||
{!disableEnterpriseFeatures && mode === "ssh" && (
|
||||
<div className="space-y-4 mt-4 p-1">
|
||||
<PaidFeaturesAlert tiers={tierMatrix.sshPam} />
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.advancedPrivateResources}
|
||||
/>
|
||||
|
||||
{/* Mode */}
|
||||
<div className="space-y-3">
|
||||
|
||||
@@ -164,7 +164,7 @@ export function RoleForm({
|
||||
}
|
||||
}, [variant, role, form]);
|
||||
|
||||
const sshDisabled = !isPaidUser(tierMatrix.sshPam);
|
||||
const sshDisabled = !isPaidUser(tierMatrix.advancedPrivateResources);
|
||||
const sshSudoMode = form.watch("sshSudoMode");
|
||||
const isAdminRole = variant === "edit" && role?.isAdmin === true;
|
||||
|
||||
@@ -319,7 +319,9 @@ export function RoleForm({
|
||||
{/* SSH tab - hidden when enterprise features are disabled */}
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<div className="space-y-4 mt-4">
|
||||
<PaidFeaturesAlert tiers={tierMatrix.sshPam} />
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.advancedPrivateResources}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="allowSsh"
|
||||
|
||||
Reference in New Issue
Block a user