mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-27 19:22:50 +00:00
Adjusting the create ui
This commit is contained in:
@@ -240,6 +240,7 @@
|
|||||||
"domainType": "Domain Type",
|
"domainType": "Domain Type",
|
||||||
"subdomain": "Subdomain",
|
"subdomain": "Subdomain",
|
||||||
"baseDomain": "Base Domain",
|
"baseDomain": "Base Domain",
|
||||||
|
"configure": "Configure",
|
||||||
"subdomnainDescription": "The subdomain where the resource will be accessible.",
|
"subdomnainDescription": "The subdomain where the resource will be accessible.",
|
||||||
"resourceRawSettings": "TCP/UDP Settings",
|
"resourceRawSettings": "TCP/UDP Settings",
|
||||||
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
|
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ import { OpenAPITags, registry } from "@server/openApi";
|
|||||||
import { build } from "@server/build";
|
import { build } from "@server/build";
|
||||||
import { createCertificate } from "#dynamic/routers/certificates/createCertificate";
|
import { createCertificate } from "#dynamic/routers/certificates/createCertificate";
|
||||||
import { getUniqueResourceName } from "@server/db/names";
|
import { getUniqueResourceName } from "@server/db/names";
|
||||||
import { validateAndConstructDomain, checkWildcardDomainConflict } from "@server/lib/domainUtils";
|
import {
|
||||||
|
validateAndConstructDomain,
|
||||||
|
checkWildcardDomainConflict
|
||||||
|
} from "@server/lib/domainUtils";
|
||||||
import { isSubscribed } from "#dynamic/lib/isSubscribed";
|
import { isSubscribed } from "#dynamic/lib/isSubscribed";
|
||||||
import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed";
|
import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed";
|
||||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
@@ -40,7 +43,8 @@ const createHttpResourceSchema = z
|
|||||||
protocol: z.enum(["tcp", "udp"]),
|
protocol: z.enum(["tcp", "udp"]),
|
||||||
domainId: z.string(),
|
domainId: z.string(),
|
||||||
stickySession: z.boolean().optional(),
|
stickySession: z.boolean().optional(),
|
||||||
postAuthPath: z.string().nullable().optional()
|
postAuthPath: z.string().nullable().optional(),
|
||||||
|
browserAccessType: z.enum(["http", "ssh", "rdp", "vnc"]).optional()
|
||||||
})
|
})
|
||||||
.refine(
|
.refine(
|
||||||
(data) => {
|
(data) => {
|
||||||
@@ -198,7 +202,7 @@ async function createHttpResource(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { name, domainId, postAuthPath } = parsedBody.data;
|
const { name, domainId, postAuthPath, browserAccessType } = parsedBody.data;
|
||||||
const subdomain = parsedBody.data.subdomain;
|
const subdomain = parsedBody.data.subdomain;
|
||||||
const stickySession = parsedBody.data.stickySession;
|
const stickySession = parsedBody.data.stickySession;
|
||||||
|
|
||||||
@@ -323,6 +327,7 @@ async function createHttpResource(
|
|||||||
name,
|
name,
|
||||||
subdomain: finalSubdomain,
|
subdomain: finalSubdomain,
|
||||||
http: true,
|
http: true,
|
||||||
|
browserAccessType: browserAccessType,
|
||||||
protocol: "tcp",
|
protocol: "tcp",
|
||||||
ssl: true,
|
ssl: true,
|
||||||
stickySession: stickySession,
|
stickySession: stickySession,
|
||||||
|
|||||||
@@ -121,10 +121,6 @@ export default function ReverseProxyTargetsPage(props: {
|
|||||||
const params = use(props.params);
|
const params = use(props.params);
|
||||||
const { resource, updateResource } = useResourceContext();
|
const { resource, updateResource } = useResourceContext();
|
||||||
|
|
||||||
const [targetMode, setTargetMode] = useState<
|
|
||||||
"http" | "ssh" | "rdp" | "vnc"
|
|
||||||
>((resource.browserAccessType as "http" | "ssh" | "rdp" | "vnc") || "http");
|
|
||||||
|
|
||||||
const { data: remoteTargets = [], isLoading: isLoadingTargets } = useQuery(
|
const { data: remoteTargets = [], isLoading: isLoadingTargets } = useQuery(
|
||||||
resourceQueries.resourceTargets({
|
resourceQueries.resourceTargets({
|
||||||
resourceId: resource.resourceId
|
resourceId: resource.resourceId
|
||||||
@@ -141,12 +137,10 @@ export default function ReverseProxyTargetsPage(props: {
|
|||||||
orgId={params.orgId}
|
orgId={params.orgId}
|
||||||
initialTargets={remoteTargets}
|
initialTargets={remoteTargets}
|
||||||
resource={resource}
|
resource={resource}
|
||||||
targetMode={targetMode}
|
|
||||||
setTargetMode={setTargetMode}
|
|
||||||
updateResource={updateResource}
|
updateResource={updateResource}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{resource.http && targetMode === "http" && (
|
{resource.http && (
|
||||||
<ProxyResourceHttpForm
|
<ProxyResourceHttpForm
|
||||||
resource={resource}
|
resource={resource}
|
||||||
updateResource={updateResource}
|
updateResource={updateResource}
|
||||||
@@ -167,15 +161,11 @@ function ProxyResourceTargetsForm({
|
|||||||
orgId,
|
orgId,
|
||||||
initialTargets,
|
initialTargets,
|
||||||
resource,
|
resource,
|
||||||
targetMode,
|
|
||||||
setTargetMode,
|
|
||||||
updateResource
|
updateResource
|
||||||
}: {
|
}: {
|
||||||
initialTargets: LocalTarget[];
|
initialTargets: LocalTarget[];
|
||||||
orgId: string;
|
orgId: string;
|
||||||
resource: GetResourceResponse;
|
resource: GetResourceResponse;
|
||||||
targetMode: "http" | "ssh" | "rdp" | "vnc";
|
|
||||||
setTargetMode: (mode: "http" | "ssh" | "rdp" | "vnc") => void;
|
|
||||||
updateResource: ResourceContextType["updateResource"];
|
updateResource: ResourceContextType["updateResource"];
|
||||||
}) {
|
}) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
@@ -310,7 +300,6 @@ function ProxyResourceTargetsForm({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!bgTargetsResponse?.targets?.length) return;
|
if (!bgTargetsResponse?.targets?.length) return;
|
||||||
const bgt = bgTargetsResponse.targets[0];
|
const bgt = bgTargetsResponse.targets[0];
|
||||||
setTargetMode(bgt.type as "ssh" | "rdp" | "vnc");
|
|
||||||
setBgDestination(bgt.destination);
|
setBgDestination(bgt.destination);
|
||||||
setBgDestinationPort(String(bgt.destinationPort));
|
setBgDestinationPort(String(bgt.destinationPort));
|
||||||
setBgSiteId(bgt.siteId);
|
setBgSiteId(bgt.siteId);
|
||||||
@@ -781,55 +770,6 @@ function ProxyResourceTargetsForm({
|
|||||||
const [, formAction, isSubmitting] = useActionState(saveTargets, null);
|
const [, formAction, isSubmitting] = useActionState(saveTargets, null);
|
||||||
|
|
||||||
async function saveTargets() {
|
async function saveTargets() {
|
||||||
if (targetMode !== "http") {
|
|
||||||
try {
|
|
||||||
if (!bgDestination || !bgDestinationPort) {
|
|
||||||
if (bgTargetId) {
|
|
||||||
await api.delete(
|
|
||||||
`/org/${orgId}/browser-gateway-target/${bgTargetId}`
|
|
||||||
);
|
|
||||||
setBgTargetId(null);
|
|
||||||
}
|
|
||||||
} else if (bgTargetId) {
|
|
||||||
await api.post(
|
|
||||||
`/org/${orgId}/browser-gateway-target/${bgTargetId}`,
|
|
||||||
{
|
|
||||||
type: targetMode,
|
|
||||||
destination: bgDestination,
|
|
||||||
destinationPort: Number(bgDestinationPort),
|
|
||||||
siteId: bgSiteId
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const res = await api.put(
|
|
||||||
`/org/${orgId}/resource/${resource.resourceId}/browser-gateway-target`,
|
|
||||||
{
|
|
||||||
siteId: bgSiteId ?? sites[0]?.siteId,
|
|
||||||
type: targetMode,
|
|
||||||
destination: bgDestination,
|
|
||||||
destinationPort: Number(bgDestinationPort)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
setBgTargetId(res.data.data.browserGatewayTargetId);
|
|
||||||
}
|
|
||||||
toast({
|
|
||||||
title: t("settingsUpdated"),
|
|
||||||
description: t("settingsUpdatedDescription")
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: t("settingsErrorUpdate"),
|
|
||||||
description: formatAxiosError(
|
|
||||||
err,
|
|
||||||
t("settingsErrorUpdateDescription")
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate that no targets have blank IPs or invalid ports
|
// Validate that no targets have blank IPs or invalid ports
|
||||||
const targetsWithInvalidFields = targets.filter(
|
const targetsWithInvalidFields = targets.filter(
|
||||||
(target) =>
|
(target) =>
|
||||||
@@ -944,187 +884,102 @@ function ProxyResourceTargetsForm({
|
|||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<div className="flex items-center gap-3 mb-4">
|
{targets.length > 0 ? (
|
||||||
<span className="text-sm font-medium">Target Type</span>
|
|
||||||
<Select
|
|
||||||
value={targetMode}
|
|
||||||
onValueChange={async (v) => {
|
|
||||||
const mode = v as
|
|
||||||
| "http"
|
|
||||||
| "ssh"
|
|
||||||
| "rdp"
|
|
||||||
| "vnc";
|
|
||||||
setTargetMode(mode);
|
|
||||||
try {
|
|
||||||
await api.post(
|
|
||||||
`/resource/${resource.resourceId}`,
|
|
||||||
{ browserAccessType: mode }
|
|
||||||
);
|
|
||||||
updateResource({ browserAccessType: mode });
|
|
||||||
} catch (err) {
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: t("settingsErrorUpdate"),
|
|
||||||
description: formatAxiosError(
|
|
||||||
err,
|
|
||||||
t("settingsErrorUpdateDescription")
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="w-36">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="http">HTTP</SelectItem>
|
|
||||||
<SelectItem value="ssh">SSH</SelectItem>
|
|
||||||
<SelectItem value="rdp">RDP</SelectItem>
|
|
||||||
<SelectItem value="vnc">VNC</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
{targetMode === "http" ? (
|
|
||||||
<>
|
<>
|
||||||
{targets.length > 0 ? (
|
<div className="overflow-x-auto">
|
||||||
<>
|
<Table>
|
||||||
<div className="overflow-x-auto">
|
<TableHeader>
|
||||||
<Table>
|
{table
|
||||||
<TableHeader>
|
.getHeaderGroups()
|
||||||
{table
|
.map((headerGroup) => (
|
||||||
.getHeaderGroups()
|
<TableRow key={headerGroup.id}>
|
||||||
.map((headerGroup) => (
|
{headerGroup.headers.map(
|
||||||
<TableRow
|
(header) => {
|
||||||
key={headerGroup.id}
|
const isActionsColumn =
|
||||||
>
|
header.column
|
||||||
{headerGroup.headers.map(
|
.id ===
|
||||||
(header) => {
|
"actions";
|
||||||
const isActionsColumn =
|
return (
|
||||||
header
|
<TableHead
|
||||||
.column
|
key={
|
||||||
.id ===
|
header.id
|
||||||
"actions";
|
}
|
||||||
return (
|
className={
|
||||||
<TableHead
|
isActionsColumn
|
||||||
key={
|
? "sticky right-0 z-10 w-auto min-w-fit bg-card"
|
||||||
header.id
|
: ""
|
||||||
}
|
}
|
||||||
className={
|
>
|
||||||
isActionsColumn
|
{header.isPlaceholder
|
||||||
? "sticky right-0 z-10 w-auto min-w-fit bg-card"
|
? null
|
||||||
: ""
|
: flexRender(
|
||||||
}
|
header
|
||||||
>
|
.column
|
||||||
{header.isPlaceholder
|
.columnDef
|
||||||
? null
|
.header,
|
||||||
: flexRender(
|
header.getContext()
|
||||||
header
|
)}
|
||||||
.column
|
</TableHead>
|
||||||
.columnDef
|
);
|
||||||
.header,
|
}
|
||||||
header.getContext()
|
)}
|
||||||
)}
|
</TableRow>
|
||||||
</TableHead>
|
))}
|
||||||
);
|
</TableHeader>
|
||||||
}
|
<TableBody>
|
||||||
)}
|
{table.getRowModel().rows?.length ? (
|
||||||
</TableRow>
|
table
|
||||||
))}
|
.getRowModel()
|
||||||
</TableHeader>
|
.rows.map((row) => (
|
||||||
<TableBody>
|
<TableRow key={row.id}>
|
||||||
{table.getRowModel().rows
|
{row
|
||||||
?.length ? (
|
.getVisibleCells()
|
||||||
table
|
.map((cell) => {
|
||||||
.getRowModel()
|
const isActionsColumn =
|
||||||
.rows.map((row) => (
|
cell.column
|
||||||
<TableRow
|
.id ===
|
||||||
key={row.id}
|
"actions";
|
||||||
>
|
return (
|
||||||
{row
|
<TableCell
|
||||||
.getVisibleCells()
|
key={
|
||||||
.map(
|
cell.id
|
||||||
(
|
|
||||||
cell
|
|
||||||
) => {
|
|
||||||
const isActionsColumn =
|
|
||||||
cell
|
|
||||||
.column
|
|
||||||
.id ===
|
|
||||||
"actions";
|
|
||||||
return (
|
|
||||||
<TableCell
|
|
||||||
key={
|
|
||||||
cell.id
|
|
||||||
}
|
|
||||||
className={
|
|
||||||
isActionsColumn
|
|
||||||
? "sticky right-0 z-10 w-auto min-w-fit bg-card"
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{flexRender(
|
|
||||||
cell
|
|
||||||
.column
|
|
||||||
.columnDef
|
|
||||||
.cell,
|
|
||||||
cell.getContext()
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
)}
|
className={
|
||||||
</TableRow>
|
isActionsColumn
|
||||||
))
|
? "sticky right-0 z-10 w-auto min-w-fit bg-card"
|
||||||
) : (
|
: ""
|
||||||
<TableRow>
|
}
|
||||||
<TableCell
|
>
|
||||||
colSpan={
|
{flexRender(
|
||||||
columns.length
|
cell
|
||||||
}
|
.column
|
||||||
className="h-24 text-center"
|
.columnDef
|
||||||
>
|
.cell,
|
||||||
{t("targetNoOne")}
|
cell.getContext()
|
||||||
</TableCell>
|
)}
|
||||||
|
</TableCell>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)}
|
))
|
||||||
</TableBody>
|
) : (
|
||||||
{/* <TableCaption> */}
|
<TableRow>
|
||||||
{/* {t('targetNoOneDescription')} */}
|
<TableCell
|
||||||
{/* </TableCaption> */}
|
colSpan={columns.length}
|
||||||
</Table>
|
className="h-24 text-center"
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between mb-4">
|
|
||||||
<div className="flex items-center justify-between w-full gap-2">
|
|
||||||
<Button
|
|
||||||
onClick={addNewTarget}
|
|
||||||
variant="outline"
|
|
||||||
>
|
|
||||||
<Plus className="h-4 w-4 mr-2" />
|
|
||||||
{t("addTarget")}
|
|
||||||
</Button>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Switch
|
|
||||||
id="advanced-mode-toggle"
|
|
||||||
checked={isAdvancedMode}
|
|
||||||
onCheckedChange={
|
|
||||||
setIsAdvancedMode
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="advanced-mode-toggle"
|
|
||||||
className="text-sm"
|
|
||||||
>
|
>
|
||||||
{t("advancedMode")}
|
{t("targetNoOne")}
|
||||||
</label>
|
</TableCell>
|
||||||
</div>
|
</TableRow>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</TableBody>
|
||||||
</>
|
{/* <TableCaption> */}
|
||||||
) : (
|
{/* {t('targetNoOneDescription')} */}
|
||||||
<div className="text-center py-8 border-2 border-dashed border-muted rounded-lg p-4">
|
{/* </TableCaption> */}
|
||||||
<p className="text-muted-foreground mb-4">
|
</Table>
|
||||||
{t("targetNoOne")}
|
</div>
|
||||||
</p>
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<div className="flex items-center justify-between w-full gap-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={addNewTarget}
|
onClick={addNewTarget}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -1132,91 +987,50 @@ function ProxyResourceTargetsForm({
|
|||||||
<Plus className="h-4 w-4 mr-2" />
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
{t("addTarget")}
|
{t("addTarget")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
<div className="flex items-center gap-2">
|
||||||
)}
|
<Switch
|
||||||
{build === "saas" &&
|
id="advanced-mode-toggle"
|
||||||
targets.length > 1 &&
|
checked={isAdvancedMode}
|
||||||
new Set(targets.map((t) => t.siteId)).size >
|
onCheckedChange={setIsAdvancedMode}
|
||||||
1 && (
|
/>
|
||||||
<p className="text-sm text-muted-foreground mt-3">
|
<label
|
||||||
{t("proxyMultiSiteRoundRobinNodeHelp")}{" "}
|
htmlFor="advanced-mode-toggle"
|
||||||
<a
|
className="text-sm"
|
||||||
href="https://docs.pangolin.net/manage/resources/public/targets#distributing-sites-load-across-servers"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-primary hover:underline inline-flex items-center gap-1"
|
|
||||||
>
|
>
|
||||||
{t("learnMore")}
|
{t("advancedMode")}
|
||||||
<ExternalLink className="size-3.5 shrink-0" />
|
</label>
|
||||||
</a>
|
</div>
|
||||||
.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="text-sm font-medium">
|
|
||||||
Destination
|
|
||||||
</label>
|
|
||||||
<Input
|
|
||||||
placeholder="192.168.1.1"
|
|
||||||
value={bgDestination}
|
|
||||||
onChange={(e) =>
|
|
||||||
setBgDestination(e.target.value)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="text-sm font-medium">
|
|
||||||
Port
|
|
||||||
</label>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder={
|
|
||||||
targetMode === "rdp"
|
|
||||||
? "3389"
|
|
||||||
: targetMode === "ssh"
|
|
||||||
? "22"
|
|
||||||
: "5900"
|
|
||||||
}
|
|
||||||
value={bgDestinationPort}
|
|
||||||
onChange={(e) =>
|
|
||||||
setBgDestinationPort(e.target.value)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{sites.length > 1 && (
|
</>
|
||||||
<div className="space-y-2">
|
) : (
|
||||||
<label className="text-sm font-medium">
|
<div className="text-center py-8 border-2 border-dashed border-muted rounded-lg p-4">
|
||||||
Site
|
<p className="text-muted-foreground mb-4">
|
||||||
</label>
|
{t("targetNoOne")}
|
||||||
<Select
|
</p>
|
||||||
value={bgSiteId ? String(bgSiteId) : ""}
|
<Button onClick={addNewTarget} variant="outline">
|
||||||
onValueChange={(v) =>
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
setBgSiteId(Number(v))
|
{t("addTarget")}
|
||||||
}
|
</Button>
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Select a site" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{sites.map((site) => (
|
|
||||||
<SelectItem
|
|
||||||
key={site.siteId}
|
|
||||||
value={String(site.siteId)}
|
|
||||||
>
|
|
||||||
{site.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{build === "saas" &&
|
||||||
|
targets.length > 1 &&
|
||||||
|
new Set(targets.map((t) => t.siteId)).size > 1 && (
|
||||||
|
<p className="text-sm text-muted-foreground mt-3">
|
||||||
|
{t("proxyMultiSiteRoundRobinNodeHelp")}{" "}
|
||||||
|
<a
|
||||||
|
href="https://docs.pangolin.net/manage/resources/public/targets#distributing-sites-load-across-servers"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-primary hover:underline inline-flex items-center gap-1"
|
||||||
|
>
|
||||||
|
{t("learnMore")}
|
||||||
|
<ExternalLink className="size-3.5 shrink-0" />
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</SettingsSectionBody>
|
</SettingsSectionBody>
|
||||||
|
|
||||||
<form className="self-end mt-4" action={formAction}>
|
<form className="self-end mt-4" action={formAction}>
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ export default function Page() {
|
|||||||
|
|
||||||
// SSH-specific state
|
// SSH-specific state
|
||||||
const [sshServerMode, setSshServerMode] = useState<"standard" | "native">(
|
const [sshServerMode, setSshServerMode] = useState<"standard" | "native">(
|
||||||
"standard"
|
"native"
|
||||||
);
|
);
|
||||||
const [pamMode, setPamMode] = useState<"passthrough" | "push">(
|
const [pamMode, setPamMode] = useState<"passthrough" | "push">(
|
||||||
"passthrough"
|
"passthrough"
|
||||||
@@ -544,7 +544,8 @@ export default function Page() {
|
|||||||
try {
|
try {
|
||||||
const payload: any = {
|
const payload: any = {
|
||||||
name: baseData.name,
|
name: baseData.name,
|
||||||
http: isHttpResource
|
http: isHttpResource,
|
||||||
|
browserAccessType: resourceType
|
||||||
};
|
};
|
||||||
|
|
||||||
let sanitizedSubdomain: string | undefined;
|
let sanitizedSubdomain: string | undefined;
|
||||||
@@ -1265,7 +1266,7 @@ export default function Page() {
|
|||||||
{t("resourceCreateGeneral")}
|
{t("resourceCreateGeneral")}
|
||||||
</SettingsSectionTitle>
|
</SettingsSectionTitle>
|
||||||
<SettingsSectionDescription>
|
<SettingsSectionDescription>
|
||||||
{t("resourceCreateDescription")}
|
{t("resourceCreateGeneralDescription")}
|
||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
|
|||||||
Reference in New Issue
Block a user