mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-18 18:23:18 +02:00
Merge branch 'free-me-up' into aig
This commit is contained in:
@@ -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}
|
||||
/>
|
||||
</SettingsFormCell>
|
||||
@@ -135,7 +131,6 @@ export default function PrivateResourceHttpPage() {
|
||||
type="submit"
|
||||
form="private-resource-http-form"
|
||||
loading={saveLoading}
|
||||
disabled={httpSectionDisabled}
|
||||
>
|
||||
{t("saveSettings")}
|
||||
</Button>
|
||||
|
||||
@@ -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 (
|
||||
<SettingsContainer>
|
||||
<PaidFeaturesAlert tiers={tierMatrix.advancedPrivateResources} />
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
@@ -161,68 +155,56 @@ export default function PrivateResourceSshPage() {
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
|
||||
<fieldset
|
||||
disabled={sshSectionDisabled}
|
||||
className={
|
||||
sshSectionDisabled
|
||||
? "opacity-50 pointer-events-none"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
<Form {...form}>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<SettingsFormGrid>
|
||||
<SshServerSettingsFields
|
||||
idPrefix="private-ssh-edit"
|
||||
pamMode={pamMode}
|
||||
standardDaemonLocation={
|
||||
standardDaemonLocation
|
||||
}
|
||||
authDaemonPort={authDaemonPort}
|
||||
onPamModeChange={handlePamModeChange}
|
||||
onStandardDaemonLocationChange={
|
||||
handleDaemonLocationChange
|
||||
}
|
||||
onAuthDaemonPortChange={(value) =>
|
||||
form.setValue(
|
||||
"authDaemonPort",
|
||||
value,
|
||||
{ shouldValidate: true }
|
||||
)
|
||||
}
|
||||
authDaemonPortError={
|
||||
form.formState.errors.authDaemonPort
|
||||
?.message
|
||||
}
|
||||
sshServerMode={sshServerMode}
|
||||
serverModeDisplay="badge"
|
||||
/>
|
||||
<PrivateResourceSshFields
|
||||
control={asAnyControl(form.control)}
|
||||
setValue={asAnySetValue(form.setValue)}
|
||||
watch={asAnyWatch(form.watch)}
|
||||
orgId={siteResource.orgId}
|
||||
selectedSites={selectedSites}
|
||||
onSelectedSitesChange={setSelectedSites}
|
||||
showSshSettings={false}
|
||||
embedInParentGrid
|
||||
showPaidFeaturesAlert={false}
|
||||
isNativeSsh={isNative}
|
||||
/>
|
||||
</SettingsFormGrid>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
<Form {...form}>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<SettingsFormGrid>
|
||||
<SshServerSettingsFields
|
||||
idPrefix="private-ssh-edit"
|
||||
pamMode={pamMode}
|
||||
standardDaemonLocation={
|
||||
standardDaemonLocation
|
||||
}
|
||||
authDaemonPort={authDaemonPort}
|
||||
onPamModeChange={handlePamModeChange}
|
||||
onStandardDaemonLocationChange={
|
||||
handleDaemonLocationChange
|
||||
}
|
||||
onAuthDaemonPortChange={(value) =>
|
||||
form.setValue("authDaemonPort", value, {
|
||||
shouldValidate: true
|
||||
})
|
||||
}
|
||||
authDaemonPortError={
|
||||
form.formState.errors.authDaemonPort
|
||||
?.message
|
||||
}
|
||||
sshServerMode={sshServerMode}
|
||||
serverModeDisplay="badge"
|
||||
/>
|
||||
<PrivateResourceSshFields
|
||||
control={asAnyControl(form.control)}
|
||||
setValue={asAnySetValue(form.setValue)}
|
||||
watch={asAnyWatch(form.watch)}
|
||||
orgId={siteResource.orgId}
|
||||
selectedSites={selectedSites}
|
||||
onSelectedSitesChange={setSelectedSites}
|
||||
showSshSettings={false}
|
||||
embedInParentGrid
|
||||
isNativeSsh={isNative}
|
||||
/>
|
||||
</SettingsFormGrid>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
|
||||
<SettingsSectionFooter>
|
||||
<form action={formAction}>
|
||||
<Button type="submit" loading={saveLoading}>
|
||||
{t("saveSettings")}
|
||||
</Button>
|
||||
</form>
|
||||
</SettingsSectionFooter>
|
||||
</Form>
|
||||
</fieldset>
|
||||
<SettingsSectionFooter>
|
||||
<form action={formAction}>
|
||||
<Button type="submit" loading={saveLoading}>
|
||||
{t("saveSettings")}
|
||||
</Button>
|
||||
</form>
|
||||
</SettingsSectionFooter>
|
||||
</Form>
|
||||
</SettingsSection>
|
||||
</SettingsContainer>
|
||||
);
|
||||
|
||||
@@ -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,6 @@ export default function CreatePrivateResourcePage() {
|
||||
)}
|
||||
watch={asAnyWatch(form.watch)}
|
||||
labelPrefix="create"
|
||||
disabled={
|
||||
mode === "ssh" &&
|
||||
sshSectionDisabled
|
||||
}
|
||||
/>
|
||||
</SettingsFormCell>
|
||||
)}
|
||||
@@ -584,9 +563,6 @@ export default function CreatePrivateResourcePage() {
|
||||
{/* HTTP configuration */}
|
||||
{mode === "http" && (
|
||||
<SettingsSection>
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.advancedPrivateResources}
|
||||
/>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
{t("httpSettings")}
|
||||
@@ -597,62 +573,43 @@ export default function CreatePrivateResourcePage() {
|
||||
)}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<fieldset
|
||||
disabled={httpSectionDisabled}
|
||||
className={
|
||||
httpSectionDisabled
|
||||
? "opacity-50 pointer-events-none"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<SettingsFormGrid>
|
||||
<SettingsFormCell span="half">
|
||||
<PrivateResourceSitesField
|
||||
control={form.control}
|
||||
orgId={orgId}
|
||||
selectedSites={
|
||||
selectedSites
|
||||
}
|
||||
onSelectedSitesChange={
|
||||
setSelectedSites
|
||||
}
|
||||
/>
|
||||
</SettingsFormCell>
|
||||
<SettingsFormCell span="full">
|
||||
<PrivateResourceHttpFields
|
||||
control={asAnyControl(
|
||||
form.control
|
||||
)}
|
||||
setValue={asAnySetValue(
|
||||
form.setValue
|
||||
)}
|
||||
orgId={orgId}
|
||||
watch={asAnyWatch(
|
||||
form.watch
|
||||
)}
|
||||
disabled={
|
||||
httpSectionDisabled
|
||||
}
|
||||
labelPrefix="create"
|
||||
hideDomainPicker
|
||||
hidePaidFeaturesAlert
|
||||
/>
|
||||
</SettingsFormCell>
|
||||
</SettingsFormGrid>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
</fieldset>
|
||||
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<SettingsFormGrid>
|
||||
<SettingsFormCell span="half">
|
||||
<PrivateResourceSitesField
|
||||
control={form.control}
|
||||
orgId={orgId}
|
||||
selectedSites={selectedSites}
|
||||
onSelectedSitesChange={
|
||||
setSelectedSites
|
||||
}
|
||||
/>
|
||||
</SettingsFormCell>
|
||||
<SettingsFormCell span="full">
|
||||
<PrivateResourceHttpFields
|
||||
control={asAnyControl(
|
||||
form.control
|
||||
)}
|
||||
setValue={asAnySetValue(
|
||||
form.setValue
|
||||
)}
|
||||
orgId={orgId}
|
||||
watch={asAnyWatch(form.watch)}
|
||||
labelPrefix="create"
|
||||
hideDomainPicker
|
||||
/>
|
||||
</SettingsFormCell>
|
||||
</SettingsFormGrid>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
</SettingsSection>
|
||||
)}
|
||||
|
||||
{/* SSH server */}
|
||||
{mode === "ssh" && (
|
||||
<SettingsSection>
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.advancedPrivateResources}
|
||||
/>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
{t("sshSettings")}
|
||||
@@ -661,37 +618,22 @@ export default function CreatePrivateResourcePage() {
|
||||
{t("sshServerDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<fieldset
|
||||
disabled={sshSectionDisabled}
|
||||
className={
|
||||
sshSectionDisabled
|
||||
? "opacity-50 pointer-events-none"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<PrivateResourceSshFields
|
||||
control={asAnyControl(form.control)}
|
||||
setValue={asAnySetValue(
|
||||
form.setValue
|
||||
)}
|
||||
watch={asAnyWatch(form.watch)}
|
||||
orgId={orgId}
|
||||
disabled={sshSectionDisabled}
|
||||
selectedSites={selectedSites}
|
||||
onSelectedSitesChange={
|
||||
setSelectedSites
|
||||
}
|
||||
labelPrefix="create"
|
||||
showSshSettings={true}
|
||||
layout="wizard"
|
||||
showPaidFeaturesAlert={false}
|
||||
hideAlias
|
||||
/>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
</fieldset>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<PrivateResourceSshFields
|
||||
control={asAnyControl(form.control)}
|
||||
setValue={asAnySetValue(form.setValue)}
|
||||
watch={asAnyWatch(form.watch)}
|
||||
orgId={orgId}
|
||||
selectedSites={selectedSites}
|
||||
onSelectedSitesChange={setSelectedSites}
|
||||
labelPrefix="create"
|
||||
showSshSettings={true}
|
||||
layout="wizard"
|
||||
hideAlias
|
||||
/>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
</SettingsSection>
|
||||
)}
|
||||
|
||||
@@ -776,7 +718,7 @@ export default function CreatePrivateResourcePage() {
|
||||
<Button
|
||||
type="submit"
|
||||
form="create-private-resource-form"
|
||||
disabled={submitDisabled}
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
{t("createInternalResourceDialogCreateResource")}
|
||||
|
||||
@@ -161,7 +161,7 @@ export default function ResourceMaintenancePage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isMaintenanceDisabled = !isPaidUser(tierMatrix.maintencePage);
|
||||
const isMaintenanceDisabled = !isPaidUser(tierMatrix.maintenancePage);
|
||||
|
||||
const maintenanceModeTypeOptions: StrategyOption<
|
||||
"automatic" | "forced"
|
||||
@@ -180,7 +180,7 @@ export default function ResourceMaintenancePage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<PaidFeaturesAlert tiers={tierMatrix.maintencePage} />
|
||||
<PaidFeaturesAlert tiers={tierMatrix.maintenancePage} />
|
||||
<div
|
||||
className={
|
||||
isMaintenanceDisabled
|
||||
|
||||
@@ -55,11 +55,7 @@ export default function RdpSettingsPage(props: {
|
||||
}) {
|
||||
const params = use(props.params);
|
||||
const { resource, updateResource } = useResourceContext();
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
const api = createApiClient(useEnvContext());
|
||||
const disabled = !isPaidUser(
|
||||
tierMatrix[TierFeature.AdvancedPublicResources]
|
||||
);
|
||||
|
||||
const { data: targetsResponse, isLoading: isLoadingTargets } = useQuery({
|
||||
queryKey: ["resourceTargets", resource.resourceId, params.orgId, "rdp"],
|
||||
@@ -75,14 +71,10 @@ export default function RdpSettingsPage(props: {
|
||||
|
||||
return (
|
||||
<SettingsContainer>
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix[TierFeature.AdvancedPublicResources]}
|
||||
/>
|
||||
<RdpServerForm
|
||||
orgId={params.orgId}
|
||||
resource={resource}
|
||||
updateResource={updateResource}
|
||||
disabled={disabled}
|
||||
targetsResponse={targetsResponse ?? { targets: [] }}
|
||||
/>
|
||||
</SettingsContainer>
|
||||
@@ -92,13 +84,11 @@ export default function RdpSettingsPage(props: {
|
||||
function RdpServerForm({
|
||||
orgId,
|
||||
resource,
|
||||
disabled,
|
||||
targetsResponse
|
||||
}: {
|
||||
orgId: string;
|
||||
resource: GetResourceResponse;
|
||||
updateResource: ResourceContextType["updateResource"];
|
||||
disabled: boolean;
|
||||
targetsResponse: ResourceTargetsResponse;
|
||||
}) {
|
||||
const t = useTranslations();
|
||||
@@ -215,10 +205,6 @@ function RdpServerForm({
|
||||
{t("rdpServerDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<fieldset
|
||||
disabled={disabled}
|
||||
className={disabled ? "opacity-50 pointer-events-none" : ""}
|
||||
>
|
||||
<Form {...form}>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
@@ -244,7 +230,6 @@ function RdpServerForm({
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
</fieldset>
|
||||
</SettingsSection>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,11 +75,7 @@ export default function SshSettingsPage(props: {
|
||||
}) {
|
||||
const params = use(props.params);
|
||||
const { resource, updateResource } = useResourceContext();
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
const api = createApiClient(useEnvContext());
|
||||
const disabled = !isPaidUser(
|
||||
tierMatrix[TierFeature.AdvancedPublicResources]
|
||||
);
|
||||
|
||||
const { data: targetsResponse, isLoading: isLoadingTargets } = useQuery({
|
||||
queryKey: ["resourceTargets", resource.resourceId, params.orgId, "ssh"],
|
||||
@@ -95,14 +91,10 @@ export default function SshSettingsPage(props: {
|
||||
|
||||
return (
|
||||
<SettingsContainer>
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix[TierFeature.AdvancedPublicResources]}
|
||||
/>
|
||||
<SshServerForm
|
||||
orgId={params.orgId}
|
||||
resource={resource}
|
||||
updateResource={updateResource}
|
||||
disabled={disabled}
|
||||
targetsResponse={targetsResponse ?? { targets: [] }}
|
||||
/>
|
||||
</SettingsContainer>
|
||||
@@ -113,13 +105,11 @@ function SshServerForm({
|
||||
orgId,
|
||||
resource,
|
||||
updateResource,
|
||||
disabled,
|
||||
targetsResponse
|
||||
}: {
|
||||
orgId: string;
|
||||
resource: GetResourceResponse;
|
||||
updateResource: ResourceContextType["updateResource"];
|
||||
disabled: boolean;
|
||||
targetsResponse: ResourceTargetsResponse;
|
||||
}) {
|
||||
const t = useTranslations();
|
||||
@@ -375,10 +365,6 @@ function SshServerForm({
|
||||
{t("sshServerDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<fieldset
|
||||
disabled={disabled}
|
||||
className={disabled ? "opacity-50 pointer-events-none" : ""}
|
||||
>
|
||||
<Form {...form}>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
@@ -530,7 +516,6 @@ function SshServerForm({
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
</fieldset>
|
||||
</SettingsSection>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,11 +55,7 @@ export default function VncSettingsPage(props: {
|
||||
}) {
|
||||
const params = use(props.params);
|
||||
const { resource, updateResource } = useResourceContext();
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
const api = createApiClient(useEnvContext());
|
||||
const disabled = !isPaidUser(
|
||||
tierMatrix[TierFeature.AdvancedPublicResources]
|
||||
);
|
||||
|
||||
const { data: targetsResponse, isLoading: isLoadingTargets } = useQuery({
|
||||
queryKey: ["resourceTargets", resource.resourceId, params.orgId, "vnc"],
|
||||
@@ -75,14 +71,10 @@ export default function VncSettingsPage(props: {
|
||||
|
||||
return (
|
||||
<SettingsContainer>
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix[TierFeature.AdvancedPublicResources]}
|
||||
/>
|
||||
<VncServerForm
|
||||
orgId={params.orgId}
|
||||
resource={resource}
|
||||
updateResource={updateResource}
|
||||
disabled={disabled}
|
||||
targetsResponse={targetsResponse ?? { targets: [] }}
|
||||
/>
|
||||
</SettingsContainer>
|
||||
@@ -92,13 +84,11 @@ export default function VncSettingsPage(props: {
|
||||
function VncServerForm({
|
||||
orgId,
|
||||
resource,
|
||||
disabled,
|
||||
targetsResponse
|
||||
}: {
|
||||
orgId: string;
|
||||
resource: GetResourceResponse;
|
||||
updateResource: ResourceContextType["updateResource"];
|
||||
disabled: boolean;
|
||||
targetsResponse: ResourceTargetsResponse;
|
||||
}) {
|
||||
const t = useTranslations();
|
||||
@@ -215,10 +205,6 @@ function VncServerForm({
|
||||
{t("vncServerDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<fieldset
|
||||
disabled={disabled}
|
||||
className={disabled ? "opacity-50 pointer-events-none" : ""}
|
||||
>
|
||||
<Form {...form}>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
@@ -244,7 +230,6 @@ function VncServerForm({
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
</fieldset>
|
||||
</SettingsSection>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -239,14 +239,6 @@ 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[]>([]);
|
||||
const [selectedProviders, setSelectedProviders] = useState<
|
||||
@@ -1056,14 +1048,6 @@ export default function Page() {
|
||||
{/* SSH Server Section */}
|
||||
{resourceType === "ssh" && (
|
||||
<SettingsSection>
|
||||
<PaidFeaturesAlert
|
||||
tiers={
|
||||
tierMatrix[
|
||||
TierFeature
|
||||
.AdvancedPublicResources
|
||||
]
|
||||
}
|
||||
/>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
{t("sshServer")}
|
||||
@@ -1072,14 +1056,7 @@ export default function Page() {
|
||||
{t("sshServerDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<fieldset
|
||||
disabled={browserGatewayDisabled}
|
||||
className={
|
||||
browserGatewayDisabled
|
||||
? "opacity-50 pointer-events-none"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<SettingsFormGrid>
|
||||
@@ -1318,21 +1295,12 @@ export default function Page() {
|
||||
</SettingsFormGrid>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
</fieldset>
|
||||
</SettingsSection>
|
||||
)}
|
||||
|
||||
{/* RDP Server Section */}
|
||||
{resourceType === "rdp" && (
|
||||
<SettingsSection>
|
||||
<PaidFeaturesAlert
|
||||
tiers={
|
||||
tierMatrix[
|
||||
TierFeature
|
||||
.AdvancedPublicResources
|
||||
]
|
||||
}
|
||||
/>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
{t("rdpServer")}
|
||||
@@ -1341,14 +1309,6 @@ export default function Page() {
|
||||
{t("rdpServerDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<fieldset
|
||||
disabled={browserGatewayDisabled}
|
||||
className={
|
||||
browserGatewayDisabled
|
||||
? "opacity-50 pointer-events-none"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<Form {...bgTargetForm}>
|
||||
@@ -1365,21 +1325,12 @@ export default function Page() {
|
||||
</Form>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
</fieldset>
|
||||
</SettingsSection>
|
||||
)}
|
||||
|
||||
{/* VNC Server Section */}
|
||||
{resourceType === "vnc" && (
|
||||
<SettingsSection>
|
||||
<PaidFeaturesAlert
|
||||
tiers={
|
||||
tierMatrix[
|
||||
TierFeature
|
||||
.AdvancedPublicResources
|
||||
]
|
||||
}
|
||||
/>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
{t("vncServer")}
|
||||
@@ -1388,14 +1339,7 @@ export default function Page() {
|
||||
{t("vncServerDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<fieldset
|
||||
disabled={browserGatewayDisabled}
|
||||
className={
|
||||
browserGatewayDisabled
|
||||
? "opacity-50 pointer-events-none"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm variant="half">
|
||||
<Form {...bgTargetForm}>
|
||||
@@ -1412,7 +1356,6 @@ export default function Page() {
|
||||
</Form>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
</fieldset>
|
||||
</SettingsSection>
|
||||
)}
|
||||
|
||||
@@ -1527,7 +1470,6 @@ export default function Page() {
|
||||
loading={createLoading}
|
||||
disabled={
|
||||
!areAllTargetsValid() ||
|
||||
browserGatewayDisabled ||
|
||||
createLoading
|
||||
}
|
||||
>
|
||||
|
||||
@@ -399,7 +399,7 @@ function AuthPageSettings({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{build !== "oss" && (build === "enterprise" ||
|
||||
{(build === "enterprise" ||
|
||||
!isPaidUser(
|
||||
tierMatrix.loginPageDomain
|
||||
)) &&
|
||||
|
||||
@@ -52,7 +52,7 @@ export default function CreateRoleForm({
|
||||
requireDeviceApproval: values.requireDeviceApproval,
|
||||
allowSsh: values.allowSsh
|
||||
};
|
||||
if (isPaidUser(tierMatrix.advancedPrivateResources)) {
|
||||
if (isPaidUser(tierMatrix.roleBasedSSHControls)) {
|
||||
payload.sshSudoMode = values.sshSudoMode;
|
||||
payload.sshCreateHomeDir = values.sshCreateHomeDir;
|
||||
payload.sshSudoCommands =
|
||||
|
||||
@@ -59,7 +59,7 @@ export default function EditRoleForm({
|
||||
payload.name = values.name;
|
||||
payload.description = values.description || undefined;
|
||||
}
|
||||
if (isPaidUser(tierMatrix.advancedPrivateResources)) {
|
||||
if (isPaidUser(tierMatrix.roleBasedSSHControls)) {
|
||||
payload.sshSudoMode = values.sshSudoMode;
|
||||
payload.sshCreateHomeDir = values.sshCreateHomeDir;
|
||||
payload.sshSudoCommands =
|
||||
@@ -107,10 +107,7 @@ export default function EditRoleForm({
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t("aiBudgetErrorSave"),
|
||||
description: formatAxiosError(
|
||||
e,
|
||||
t("aiBudgetErrorSave")
|
||||
)
|
||||
description: formatAxiosError(e, t("aiBudgetErrorSave"))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import DomainPicker from "@app/components/DomainPicker";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import {
|
||||
SettingsFormCell,
|
||||
SettingsFormGrid,
|
||||
@@ -25,7 +24,6 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from "@app/components/ui/select";
|
||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
import { useTranslations } from "next-intl";
|
||||
import type { Control, UseFormSetValue, UseFormWatch } from "react-hook-form";
|
||||
|
||||
@@ -49,8 +47,7 @@ export function PrivateResourceHttpFields({
|
||||
disabled = false,
|
||||
siteResourceId,
|
||||
labelPrefix = "edit",
|
||||
hideDomainPicker = false,
|
||||
hidePaidFeaturesAlert = false
|
||||
hideDomainPicker = false
|
||||
}: PrivateResourceHttpFieldsProps) {
|
||||
const t = useTranslations();
|
||||
const schemeLabelKey =
|
||||
@@ -88,14 +85,6 @@ export function PrivateResourceHttpFields({
|
||||
|
||||
return (
|
||||
<SettingsFormGrid>
|
||||
{!hidePaidFeaturesAlert && (
|
||||
<SettingsFormCell span="full">
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.advancedPrivateResources}
|
||||
/>
|
||||
</SettingsFormCell>
|
||||
)}
|
||||
|
||||
<SettingsFormCell span="quarter">
|
||||
<FormField
|
||||
control={control}
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
type LauncherAccessFields
|
||||
} from "@app/lib/launcherResourceAccess";
|
||||
import type { PrivateResourceMode } from "@app/lib/privateResourceForm";
|
||||
import { build } from "@server/build";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
type SiteResourceInfoInput = {
|
||||
@@ -121,8 +120,7 @@ export function PrivateResourceInfoSections({
|
||||
(siteResource.mode === "http" || siteResource.mode === "inference") &&
|
||||
siteResource.ssl &&
|
||||
siteResource.domainId &&
|
||||
siteResource.fullDomain &&
|
||||
build != "oss"
|
||||
siteResource.fullDomain
|
||||
);
|
||||
const showPortRestrictions =
|
||||
isPanel &&
|
||||
|
||||
@@ -38,7 +38,6 @@ type PrivateResourceSshFieldsProps = {
|
||||
labelPrefix?: "create" | "edit";
|
||||
showSshSettings?: boolean;
|
||||
layout?: "default" | "wizard";
|
||||
showPaidFeaturesAlert?: boolean;
|
||||
hideAlias?: boolean;
|
||||
embedInParentGrid?: boolean;
|
||||
isNativeSsh?: boolean;
|
||||
@@ -55,7 +54,6 @@ export function PrivateResourceSshFields({
|
||||
labelPrefix = "edit",
|
||||
showSshSettings = true,
|
||||
layout = "default",
|
||||
showPaidFeaturesAlert = true,
|
||||
hideAlias = false,
|
||||
embedInParentGrid = false,
|
||||
isNativeSsh: isNativeSshProp
|
||||
@@ -313,13 +311,6 @@ export function PrivateResourceSshFields({
|
||||
|
||||
const content: ReactNode = (
|
||||
<>
|
||||
{showPaidFeaturesAlert && layout === "default" && (
|
||||
<SettingsFormCell span="full">
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.advancedPrivateResources}
|
||||
/>
|
||||
</SettingsFormCell>
|
||||
)}
|
||||
{sshSettingsFields}
|
||||
{destinationSection}
|
||||
</>
|
||||
|
||||
@@ -429,7 +429,6 @@ export default function PrivateResourcesTable({
|
||||
const fullDomain = resourceRow.fullDomain;
|
||||
const url = `${resourceRow.ssl ? "https" : "http"}://${fullDomain}`;
|
||||
const did =
|
||||
build !== "oss" &&
|
||||
resourceRow.ssl &&
|
||||
domainId != null &&
|
||||
domainId !== "" &&
|
||||
|
||||
@@ -468,7 +468,6 @@ export default function PublicResourcesTable({
|
||||
const domainId = resourceRow.domainId;
|
||||
const certHostname = resourceRow.fullDomain;
|
||||
const showHttpsCertIndicator =
|
||||
build !== "oss" &&
|
||||
resourceRow.ssl &&
|
||||
certHostname != null &&
|
||||
certHostname !== "";
|
||||
|
||||
@@ -40,8 +40,7 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) {
|
||||
const showCertificate = !!(
|
||||
isDomainResource &&
|
||||
resource.domainId &&
|
||||
resource.fullDomain &&
|
||||
build != "oss"
|
||||
resource.fullDomain
|
||||
);
|
||||
const showType = !!(isDomainResource && resource.mode);
|
||||
const showAuth = resource.mode !== "inference";
|
||||
|
||||
+181
-183
@@ -212,7 +212,7 @@ export function RoleForm({
|
||||
}
|
||||
}, [variant, role, form]);
|
||||
|
||||
const sshDisabled = !isPaidUser(tierMatrix.advancedPrivateResources);
|
||||
const sshDisabled = !isPaidUser(tierMatrix.roleBasedSSHControls);
|
||||
const sshSudoMode = form.watch("sshSudoMode");
|
||||
const isAdminRole = variant === "edit" && role?.isAdmin === true;
|
||||
const [pendingImport, setPendingImport] =
|
||||
@@ -235,12 +235,6 @@ export function RoleForm({
|
||||
setAttemptedBudgetsSave(false);
|
||||
}, [variant, budgetsQuery.data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (sshDisabled) {
|
||||
form.setValue("allowSsh", false);
|
||||
}
|
||||
}, [sshDisabled, form]);
|
||||
|
||||
async function handleFileDrop(
|
||||
file: File,
|
||||
field: RoleTextImportField
|
||||
@@ -487,115 +481,157 @@ export function RoleForm({
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* SSH tab - hidden when enterprise features are disabled */}
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<div className="space-y-4 mt-4">
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.advancedPrivateResources}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="allowSsh"
|
||||
render={({ field }) => {
|
||||
const allowSshOptions: OptionSelectOption<
|
||||
"allow" | "disallow"
|
||||
>[] = [
|
||||
{
|
||||
value: "allow",
|
||||
label: t("roleAllowSshAllow")
|
||||
},
|
||||
{
|
||||
value: "disallow",
|
||||
label: t("roleAllowSshDisallow")
|
||||
}
|
||||
];
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("roleAllowSsh")}
|
||||
</FormLabel>
|
||||
<OptionSelect<
|
||||
"allow" | "disallow"
|
||||
>
|
||||
options={allowSshOptions}
|
||||
value={
|
||||
sshDisabled
|
||||
? "disallow"
|
||||
: field.value
|
||||
? "allow"
|
||||
: "disallow"
|
||||
}
|
||||
onChange={(v) => {
|
||||
if (sshDisabled) return;
|
||||
field.onChange(
|
||||
v === "allow"
|
||||
);
|
||||
}}
|
||||
cols={2}
|
||||
disabled={sshDisabled}
|
||||
/>
|
||||
<FormDescription>
|
||||
{t(
|
||||
"roleAllowSshDescription"
|
||||
)}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sshSudoMode"
|
||||
render={({ field }) => {
|
||||
const sudoOptions: OptionSelectOption<SshSudoMode>[] =
|
||||
[
|
||||
{
|
||||
value: "none",
|
||||
label: t("sshSudoModeNone")
|
||||
},
|
||||
{
|
||||
value: "full",
|
||||
label: t("sshSudoModeFull")
|
||||
},
|
||||
{
|
||||
value: "commands",
|
||||
label: t(
|
||||
"sshSudoModeCommands"
|
||||
)
|
||||
<div className="space-y-4 mt-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="allowSsh"
|
||||
render={({ field }) => {
|
||||
const allowSshOptions: OptionSelectOption<
|
||||
"allow" | "disallow"
|
||||
>[] = [
|
||||
{
|
||||
value: "allow",
|
||||
label: t("roleAllowSshAllow")
|
||||
},
|
||||
{
|
||||
value: "disallow",
|
||||
label: t("roleAllowSshDisallow")
|
||||
}
|
||||
];
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("roleAllowSsh")}
|
||||
</FormLabel>
|
||||
<OptionSelect<"allow" | "disallow">
|
||||
options={allowSshOptions}
|
||||
value={
|
||||
field.value
|
||||
? "allow"
|
||||
: "disallow"
|
||||
}
|
||||
];
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("sshSudoMode")}
|
||||
</FormLabel>
|
||||
<OptionSelect<SshSudoMode>
|
||||
options={sudoOptions}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
cols={3}
|
||||
disabled={sshDisabled}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{sshSudoMode === "commands" && (
|
||||
onChange={(v) => {
|
||||
field.onChange(
|
||||
v === "allow"
|
||||
);
|
||||
}}
|
||||
cols={2}
|
||||
/>
|
||||
<FormDescription>
|
||||
{t("roleAllowSshDescription")}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{/* SSH tab - hidden when enterprise features are disabled */}
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<>
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.roleBasedSSHControls}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sshSudoCommands"
|
||||
name="sshSudoMode"
|
||||
render={({ field }) => {
|
||||
const sudoOptions: OptionSelectOption<SshSudoMode>[] =
|
||||
[
|
||||
{
|
||||
value: "none",
|
||||
label: t(
|
||||
"sshSudoModeNone"
|
||||
)
|
||||
},
|
||||
{
|
||||
value: "full",
|
||||
label: t(
|
||||
"sshSudoModeFull"
|
||||
)
|
||||
},
|
||||
{
|
||||
value: "commands",
|
||||
label: t(
|
||||
"sshSudoModeCommands"
|
||||
)
|
||||
}
|
||||
];
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("sshSudoMode")}
|
||||
</FormLabel>
|
||||
<OptionSelect<SshSudoMode>
|
||||
options={sudoOptions}
|
||||
value={field.value}
|
||||
onChange={
|
||||
field.onChange
|
||||
}
|
||||
cols={3}
|
||||
disabled={sshDisabled}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{sshSudoMode === "commands" && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sshSudoCommands"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("sshSudoCommands")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
{...field}
|
||||
{...getTextImportDropHandlers(
|
||||
"sshSudoCommands"
|
||||
)}
|
||||
placeholder={
|
||||
sshDisabled
|
||||
? undefined
|
||||
: t(
|
||||
"roleTextFieldPlaceholder"
|
||||
)
|
||||
}
|
||||
disabled={
|
||||
sshDisabled
|
||||
}
|
||||
className={cn(
|
||||
"h-20 min-h-20",
|
||||
dragOverField ===
|
||||
"sshSudoCommands" &&
|
||||
"border-primary"
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{t(
|
||||
"sshSudoCommandsDescription"
|
||||
)}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sshUnixGroups"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("sshSudoCommands")}
|
||||
{t("sshUnixGroups")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
{...field}
|
||||
{...getTextImportDropHandlers(
|
||||
"sshSudoCommands"
|
||||
"sshUnixGroups"
|
||||
)}
|
||||
placeholder={
|
||||
sshDisabled
|
||||
@@ -608,97 +644,59 @@ export function RoleForm({
|
||||
className={cn(
|
||||
"h-20 min-h-20",
|
||||
dragOverField ===
|
||||
"sshSudoCommands" &&
|
||||
"sshUnixGroups" &&
|
||||
"border-primary"
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{t(
|
||||
"sshSudoCommandsDescription"
|
||||
"sshUnixGroupsDescription"
|
||||
)}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sshUnixGroups"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("sshUnixGroups")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
{...field}
|
||||
{...getTextImportDropHandlers(
|
||||
"sshUnixGroups"
|
||||
)}
|
||||
placeholder={
|
||||
sshDisabled
|
||||
? undefined
|
||||
: t(
|
||||
"roleTextFieldPlaceholder"
|
||||
)
|
||||
}
|
||||
disabled={sshDisabled}
|
||||
className={cn(
|
||||
"h-20 min-h-20",
|
||||
dragOverField ===
|
||||
"sshUnixGroups" &&
|
||||
"border-primary"
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{t("sshUnixGroupsDescription")}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sshCreateHomeDir"
|
||||
render={({ field }) => (
|
||||
<FormItem className="my-2">
|
||||
<FormControl>
|
||||
<CheckboxWithLabel
|
||||
{...field}
|
||||
value="on"
|
||||
checked={form.watch(
|
||||
"sshCreateHomeDir"
|
||||
)}
|
||||
onCheckedChange={(
|
||||
checked
|
||||
) => {
|
||||
if (
|
||||
checked !==
|
||||
"indeterminate"
|
||||
) {
|
||||
form.setValue(
|
||||
"sshCreateHomeDir",
|
||||
checked
|
||||
);
|
||||
}
|
||||
}}
|
||||
label={t(
|
||||
"sshCreateHomeDir"
|
||||
)}
|
||||
disabled={sshDisabled}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sshCreateHomeDir"
|
||||
render={({ field }) => (
|
||||
<FormItem className="my-2">
|
||||
<FormControl>
|
||||
<CheckboxWithLabel
|
||||
{...field}
|
||||
value="on"
|
||||
checked={form.watch(
|
||||
"sshCreateHomeDir"
|
||||
)}
|
||||
onCheckedChange={(
|
||||
checked
|
||||
) => {
|
||||
if (
|
||||
checked !==
|
||||
"indeterminate"
|
||||
) {
|
||||
form.setValue(
|
||||
"sshCreateHomeDir",
|
||||
checked
|
||||
);
|
||||
}
|
||||
}}
|
||||
label={t(
|
||||
"sshCreateHomeDir"
|
||||
)}
|
||||
disabled={sshDisabled}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 mt-4">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/**
|
||||
* Set a cookie on the client side in javascript code, not on the server
|
||||
* @param name
|
||||
* @param value
|
||||
* @param days
|
||||
* @param options
|
||||
*/
|
||||
export function setClientCookie(
|
||||
name: string,
|
||||
value: string,
|
||||
|
||||
Reference in New Issue
Block a user