mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-22 20:20:29 +02:00
Add tcp and udp specific pages
This commit is contained in:
+3
-1
@@ -3430,5 +3430,7 @@
|
|||||||
"memberPortalShowingResources": "Showing {start}-{end} of {total} resources",
|
"memberPortalShowingResources": "Showing {start}-{end} of {total} resources",
|
||||||
"memberPortalPrevious": "Previous",
|
"memberPortalPrevious": "Previous",
|
||||||
"memberPortalNext": "Next",
|
"memberPortalNext": "Next",
|
||||||
"httpSettings": "HTTP Settings"
|
"httpSettings": "HTTP Settings",
|
||||||
|
"tcpSettings": "TCP Settings",
|
||||||
|
"udpSettings": "UDP Settings"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -519,6 +519,46 @@ export default async function migration() {
|
|||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const existingRoleResources = await db.execute(sql`
|
||||||
|
SELECT "roleId"
|
||||||
|
FROM "roleResources"
|
||||||
|
WHERE "resourceId" = ${resource.resourceId}
|
||||||
|
`);
|
||||||
|
for (const roleRow of existingRoleResources.rows as {
|
||||||
|
roleId: number;
|
||||||
|
}[]) {
|
||||||
|
await db.execute(sql`
|
||||||
|
INSERT INTO "rolePolicies" ("roleId", "resourcePolicyId")
|
||||||
|
SELECT ${roleRow.roleId}, ${resourcePolicyId}
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM "rolePolicies"
|
||||||
|
WHERE "roleId" = ${roleRow.roleId}
|
||||||
|
AND "resourcePolicyId" = ${resourcePolicyId}
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingUserResources = await db.execute(sql`
|
||||||
|
SELECT "userId"
|
||||||
|
FROM "userResources"
|
||||||
|
WHERE "resourceId" = ${resource.resourceId}
|
||||||
|
`);
|
||||||
|
for (const userRow of existingUserResources.rows as {
|
||||||
|
userId: string;
|
||||||
|
}[]) {
|
||||||
|
await db.execute(sql`
|
||||||
|
INSERT INTO "userPolicies" ("userId", "resourcePolicyId")
|
||||||
|
SELECT ${userRow.userId}, ${resourcePolicyId}
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM "userPolicies"
|
||||||
|
WHERE "userId" = ${userRow.userId}
|
||||||
|
AND "resourcePolicyId" = ${resourcePolicyId}
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
await db.execute(sql`
|
await db.execute(sql`
|
||||||
DELETE FROM "resourcePincode"
|
DELETE FROM "resourcePincode"
|
||||||
WHERE "resourceId" = ${resource.resourceId}
|
WHERE "resourceId" = ${resource.resourceId}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ export function generateName(): string {
|
|||||||
return name.replace(/[^a-z0-9-]/g, "");
|
return name.replace(/[^a-z0-9-]/g, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await migration();
|
||||||
|
|
||||||
export default async function migration() {
|
export default async function migration() {
|
||||||
console.log(`Running setup script ${version}...`);
|
console.log(`Running setup script ${version}...`);
|
||||||
|
|
||||||
@@ -456,6 +458,42 @@ export default async function migration() {
|
|||||||
) VALUES (?, ?)`
|
) VALUES (?, ?)`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const selectRoleResources = db.prepare(
|
||||||
|
`SELECT "roleId"
|
||||||
|
FROM 'roleResources'
|
||||||
|
WHERE "resourceId" = ?`
|
||||||
|
);
|
||||||
|
const rolePolicyExists = db.prepare(
|
||||||
|
`SELECT 1
|
||||||
|
FROM 'rolePolicies'
|
||||||
|
WHERE "roleId" = ? AND "resourcePolicyId" = ?
|
||||||
|
LIMIT 1`
|
||||||
|
);
|
||||||
|
const insertRolePolicy = db.prepare(
|
||||||
|
`INSERT INTO 'rolePolicies' (
|
||||||
|
"roleId",
|
||||||
|
"resourcePolicyId"
|
||||||
|
) VALUES (?, ?)`
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectUserResources = db.prepare(
|
||||||
|
`SELECT "userId"
|
||||||
|
FROM 'userResources'
|
||||||
|
WHERE "resourceId" = ?`
|
||||||
|
);
|
||||||
|
const userPolicyExists = db.prepare(
|
||||||
|
`SELECT 1
|
||||||
|
FROM 'userPolicies'
|
||||||
|
WHERE "userId" = ? AND "resourcePolicyId" = ?
|
||||||
|
LIMIT 1`
|
||||||
|
);
|
||||||
|
const insertUserPolicy = db.prepare(
|
||||||
|
`INSERT INTO 'userPolicies' (
|
||||||
|
"userId",
|
||||||
|
"resourcePolicyId"
|
||||||
|
) VALUES (?, ?)`
|
||||||
|
);
|
||||||
|
|
||||||
const deleteResourcePincodes = db.prepare(
|
const deleteResourcePincodes = db.prepare(
|
||||||
`DELETE FROM 'resourcePincode' WHERE "resourceId" = ?`
|
`DELETE FROM 'resourcePincode' WHERE "resourceId" = ?`
|
||||||
);
|
);
|
||||||
@@ -586,6 +624,32 @@ export default async function migration() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const resourceRoles = selectRoleResources.all(
|
||||||
|
resource.resourceId
|
||||||
|
) as { roleId: number }[];
|
||||||
|
for (const role of resourceRoles) {
|
||||||
|
const exists = rolePolicyExists.get(
|
||||||
|
role.roleId,
|
||||||
|
policyId
|
||||||
|
) as { 1: number } | undefined;
|
||||||
|
if (!exists) {
|
||||||
|
insertRolePolicy.run(role.roleId, policyId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resourceUsers = selectUserResources.all(
|
||||||
|
resource.resourceId
|
||||||
|
) as { userId: string }[];
|
||||||
|
for (const user of resourceUsers) {
|
||||||
|
const exists = userPolicyExists.get(
|
||||||
|
user.userId,
|
||||||
|
policyId
|
||||||
|
) as { 1: number } | undefined;
|
||||||
|
if (!exists) {
|
||||||
|
insertUserPolicy.run(user.userId, policyId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
deleteResourcePincodes.run(resource.resourceId);
|
deleteResourcePincodes.run(resource.resourceId);
|
||||||
deleteResourcePasswords.run(resource.resourceId);
|
deleteResourcePasswords.run(resource.resourceId);
|
||||||
deleteResourceHeaderAuth.run(resource.resourceId);
|
deleteResourceHeaderAuth.run(resource.resourceId);
|
||||||
|
|||||||
@@ -101,12 +101,6 @@ export default function ReverseProxyTargetsPage(props: {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{resource.mode == "tcp" && (
|
|
||||||
<ProxyResourceProtocolForm
|
|
||||||
resource={resource}
|
|
||||||
updateResource={updateResource}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</SettingsContainer>
|
</SettingsContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -405,205 +399,4 @@ function ProxyResourceHttpForm({
|
|||||||
</SettingsSectionBody>
|
</SettingsSectionBody>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ProxyResourceProtocolForm({
|
|
||||||
resource,
|
|
||||||
updateResource
|
|
||||||
}: Pick<ResourceContextType, "resource" | "updateResource">) {
|
|
||||||
const t = useTranslations();
|
|
||||||
|
|
||||||
const api = createApiClient(useEnvContext());
|
|
||||||
|
|
||||||
const proxySettingsSchema = z.object({
|
|
||||||
setHostHeader: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.refine(
|
|
||||||
(data) => {
|
|
||||||
if (data) {
|
|
||||||
return tlsNameSchema.safeParse(data).success;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
message: t("proxyErrorInvalidHeader")
|
|
||||||
}
|
|
||||||
),
|
|
||||||
headers: z
|
|
||||||
.array(z.object({ name: z.string(), value: z.string() }))
|
|
||||||
.nullable(),
|
|
||||||
proxyProtocol: z.boolean().optional(),
|
|
||||||
proxyProtocolVersion: z.int().min(1).max(2).optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
const proxySettingsForm = useForm({
|
|
||||||
resolver: zodResolver(proxySettingsSchema),
|
|
||||||
defaultValues: {
|
|
||||||
setHostHeader: resource.setHostHeader || "",
|
|
||||||
headers: resource.headers,
|
|
||||||
proxyProtocol: resource.proxyProtocol || false,
|
|
||||||
proxyProtocolVersion: resource.proxyProtocolVersion || 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const [, formAction, isSubmitting] = useActionState(
|
|
||||||
saveProtocolSettings,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
async function saveProtocolSettings() {
|
|
||||||
const isValid = proxySettingsForm.trigger();
|
|
||||||
if (!isValid) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// For TCP/UDP resources, save proxy protocol settings
|
|
||||||
const proxyData = proxySettingsForm.getValues();
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
proxyProtocol: proxyData.proxyProtocol || false,
|
|
||||||
proxyProtocolVersion: proxyData.proxyProtocolVersion || 1
|
|
||||||
};
|
|
||||||
|
|
||||||
await api.post(`/resource/${resource.resourceId}`, payload);
|
|
||||||
|
|
||||||
updateResource({
|
|
||||||
...resource,
|
|
||||||
proxyProtocol: proxyData.proxyProtocol || false,
|
|
||||||
proxyProtocolVersion: proxyData.proxyProtocolVersion || 1
|
|
||||||
});
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: t("settingsUpdated"),
|
|
||||||
description: t("settingsUpdatedDescription")
|
|
||||||
});
|
|
||||||
|
|
||||||
router.refresh();
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: t("settingsErrorUpdate"),
|
|
||||||
description: formatAxiosError(
|
|
||||||
err,
|
|
||||||
t("settingsErrorUpdateDescription")
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SettingsSection>
|
|
||||||
<SettingsSectionHeader>
|
|
||||||
<SettingsSectionTitle>
|
|
||||||
{t("proxyProtocol")}
|
|
||||||
</SettingsSectionTitle>
|
|
||||||
<SettingsSectionDescription>
|
|
||||||
{t("proxyProtocolDescription")}
|
|
||||||
</SettingsSectionDescription>
|
|
||||||
</SettingsSectionHeader>
|
|
||||||
<SettingsSectionBody>
|
|
||||||
<SettingsSectionForm>
|
|
||||||
<Form {...proxySettingsForm}>
|
|
||||||
<form
|
|
||||||
action={formAction}
|
|
||||||
className="space-y-4"
|
|
||||||
id="proxy-protocol-settings-form"
|
|
||||||
>
|
|
||||||
<FormField
|
|
||||||
control={proxySettingsForm.control}
|
|
||||||
name="proxyProtocol"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormControl>
|
|
||||||
<SwitchInput
|
|
||||||
id="proxy-protocol-toggle"
|
|
||||||
label={t("enableProxyProtocol")}
|
|
||||||
description={t(
|
|
||||||
"proxyProtocolInfo"
|
|
||||||
)}
|
|
||||||
defaultChecked={
|
|
||||||
field.value || false
|
|
||||||
}
|
|
||||||
onCheckedChange={(val) => {
|
|
||||||
field.onChange(val);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{proxySettingsForm.watch("proxyProtocol") && (
|
|
||||||
<>
|
|
||||||
<FormField
|
|
||||||
control={proxySettingsForm.control}
|
|
||||||
name="proxyProtocolVersion"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>
|
|
||||||
{t("proxyProtocolVersion")}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Select
|
|
||||||
value={String(
|
|
||||||
field.value || 1
|
|
||||||
)}
|
|
||||||
onValueChange={(
|
|
||||||
value
|
|
||||||
) =>
|
|
||||||
field.onChange(
|
|
||||||
parseInt(
|
|
||||||
value,
|
|
||||||
10
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Select version" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="1">
|
|
||||||
{t("version1")}
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="2">
|
|
||||||
{t("version2")}
|
|
||||||
</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
<FormDescription>
|
|
||||||
{t("versionDescription")}
|
|
||||||
</FormDescription>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Alert>
|
|
||||||
<AlertTriangle className="h-4 w-4" />
|
|
||||||
<AlertDescription>
|
|
||||||
<strong>{t("warning")}:</strong>{" "}
|
|
||||||
{t("proxyProtocolWarning")}
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</SettingsSectionForm>
|
|
||||||
<form action={formAction} className="flex justify-end">
|
|
||||||
<Button
|
|
||||||
disabled={isSubmitting}
|
|
||||||
loading={isSubmitting}
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
{t("saveProxyProtocol")}
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</SettingsSectionBody>
|
|
||||||
</SettingsSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import {
|
||||||
|
SettingsContainer,
|
||||||
|
SettingsSection,
|
||||||
|
SettingsSectionBody,
|
||||||
|
SettingsSectionDescription,
|
||||||
|
SettingsSectionForm,
|
||||||
|
SettingsSectionHeader,
|
||||||
|
SettingsSectionTitle
|
||||||
|
} from "@app/components/Settings";
|
||||||
|
import { SwitchInput } from "@app/components/SwitchInput";
|
||||||
|
import { Alert, AlertDescription } from "@app/components/ui/alert";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage
|
||||||
|
} from "@app/components/ui/form";
|
||||||
|
import type { ResourceContextType } from "@app/contexts/resourceContext";
|
||||||
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
|
import { useResourceContext } from "@app/hooks/useResourceContext";
|
||||||
|
import { toast } from "@app/hooks/useToast";
|
||||||
|
import { createApiClient } from "@app/lib/api";
|
||||||
|
import { formatAxiosError } from "@app/lib/api/formatAxiosError";
|
||||||
|
import { resourceQueries } from "@app/lib/queries";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { tlsNameSchema } from "@server/lib/schemas";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import {
|
||||||
|
ProxyResourceTargetsForm
|
||||||
|
} from "@app/app/[orgId]/settings/resources/public/ProxyResourceTargetsForm";
|
||||||
|
import {
|
||||||
|
AlertTriangle,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import {
|
||||||
|
use,
|
||||||
|
useActionState,
|
||||||
|
} from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export default function ReverseProxyTargetsPage(props: {
|
||||||
|
params: Promise<{ resourceId: number; orgId: string }>;
|
||||||
|
}) {
|
||||||
|
const params = use(props.params);
|
||||||
|
const { resource, updateResource } = useResourceContext();
|
||||||
|
|
||||||
|
const { data: remoteTargets = [], isLoading: isLoadingTargets } = useQuery(
|
||||||
|
resourceQueries.resourceTargets({
|
||||||
|
resourceId: resource.resourceId
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isLoadingTargets) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsContainer>
|
||||||
|
<ProxyResourceTargetsForm
|
||||||
|
orgId={params.orgId}
|
||||||
|
isHttp={["http", "ssh", "rdp", "vnc"].includes(resource.mode)}
|
||||||
|
initialTargets={remoteTargets}
|
||||||
|
resource={resource}
|
||||||
|
updateResource={updateResource}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{resource.mode == "tcp" && (
|
||||||
|
<ProxyResourceProtocolForm
|
||||||
|
resource={resource}
|
||||||
|
updateResource={updateResource}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</SettingsContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProxyResourceProtocolForm({
|
||||||
|
resource,
|
||||||
|
updateResource
|
||||||
|
}: Pick<ResourceContextType, "resource" | "updateResource">) {
|
||||||
|
const t = useTranslations();
|
||||||
|
|
||||||
|
const api = createApiClient(useEnvContext());
|
||||||
|
|
||||||
|
const proxySettingsSchema = z.object({
|
||||||
|
setHostHeader: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.refine(
|
||||||
|
(data) => {
|
||||||
|
if (data) {
|
||||||
|
return tlsNameSchema.safeParse(data).success;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: t("proxyErrorInvalidHeader")
|
||||||
|
}
|
||||||
|
),
|
||||||
|
headers: z
|
||||||
|
.array(z.object({ name: z.string(), value: z.string() }))
|
||||||
|
.nullable(),
|
||||||
|
proxyProtocol: z.boolean().optional(),
|
||||||
|
proxyProtocolVersion: z.int().min(1).max(2).optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
const proxySettingsForm = useForm({
|
||||||
|
resolver: zodResolver(proxySettingsSchema),
|
||||||
|
defaultValues: {
|
||||||
|
setHostHeader: resource.setHostHeader || "",
|
||||||
|
headers: resource.headers,
|
||||||
|
proxyProtocol: resource.proxyProtocol || false,
|
||||||
|
proxyProtocolVersion: resource.proxyProtocolVersion || 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const [, formAction, isSubmitting] = useActionState(
|
||||||
|
saveProtocolSettings,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
async function saveProtocolSettings() {
|
||||||
|
const isValid = proxySettingsForm.trigger();
|
||||||
|
if (!isValid) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// For TCP/UDP resources, save proxy protocol settings
|
||||||
|
const proxyData = proxySettingsForm.getValues();
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
proxyProtocol: proxyData.proxyProtocol || false,
|
||||||
|
proxyProtocolVersion: proxyData.proxyProtocolVersion || 1
|
||||||
|
};
|
||||||
|
|
||||||
|
await api.post(`/resource/${resource.resourceId}`, payload);
|
||||||
|
|
||||||
|
updateResource({
|
||||||
|
...resource,
|
||||||
|
proxyProtocol: proxyData.proxyProtocol || false,
|
||||||
|
proxyProtocolVersion: proxyData.proxyProtocolVersion || 1
|
||||||
|
});
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: t("settingsUpdated"),
|
||||||
|
description: t("settingsUpdatedDescription")
|
||||||
|
});
|
||||||
|
|
||||||
|
router.refresh();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: t("settingsErrorUpdate"),
|
||||||
|
description: formatAxiosError(
|
||||||
|
err,
|
||||||
|
t("settingsErrorUpdateDescription")
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsSection>
|
||||||
|
<SettingsSectionHeader>
|
||||||
|
<SettingsSectionTitle>
|
||||||
|
{t("proxyProtocol")}
|
||||||
|
</SettingsSectionTitle>
|
||||||
|
<SettingsSectionDescription>
|
||||||
|
{t("proxyProtocolDescription")}
|
||||||
|
</SettingsSectionDescription>
|
||||||
|
</SettingsSectionHeader>
|
||||||
|
<SettingsSectionBody>
|
||||||
|
<SettingsSectionForm>
|
||||||
|
<Form {...proxySettingsForm}>
|
||||||
|
<form
|
||||||
|
action={formAction}
|
||||||
|
className="space-y-4"
|
||||||
|
id="proxy-protocol-settings-form"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={proxySettingsForm.control}
|
||||||
|
name="proxyProtocol"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<SwitchInput
|
||||||
|
id="proxy-protocol-toggle"
|
||||||
|
label={t("enableProxyProtocol")}
|
||||||
|
description={t(
|
||||||
|
"proxyProtocolInfo"
|
||||||
|
)}
|
||||||
|
defaultChecked={
|
||||||
|
field.value || false
|
||||||
|
}
|
||||||
|
onCheckedChange={(val) => {
|
||||||
|
field.onChange(val);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{proxySettingsForm.watch("proxyProtocol") && (
|
||||||
|
<>
|
||||||
|
<FormField
|
||||||
|
control={proxySettingsForm.control}
|
||||||
|
name="proxyProtocolVersion"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>
|
||||||
|
{t("proxyProtocolVersion")}
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Select
|
||||||
|
value={String(
|
||||||
|
field.value || 1
|
||||||
|
)}
|
||||||
|
onValueChange={(
|
||||||
|
value
|
||||||
|
) =>
|
||||||
|
field.onChange(
|
||||||
|
parseInt(
|
||||||
|
value,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select version" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="1">
|
||||||
|
{t("version1")}
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="2">
|
||||||
|
{t("version2")}
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
{t("versionDescription")}
|
||||||
|
</FormDescription>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Alert>
|
||||||
|
<AlertTriangle className="h-4 w-4" />
|
||||||
|
<AlertDescription>
|
||||||
|
<strong>{t("warning")}:</strong>{" "}
|
||||||
|
{t("proxyProtocolWarning")}
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</SettingsSectionForm>
|
||||||
|
<form action={formAction} className="flex justify-end">
|
||||||
|
<Button
|
||||||
|
disabled={isSubmitting}
|
||||||
|
loading={isSubmitting}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
{t("saveProxyProtocol")}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</SettingsSectionBody>
|
||||||
|
</SettingsSection>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
SettingsContainer,
|
||||||
|
} from "@app/components/Settings";
|
||||||
|
import { useResourceContext } from "@app/hooks/useResourceContext";
|
||||||
|
import { resourceQueries } from "@app/lib/queries";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import {
|
||||||
|
ProxyResourceTargetsForm
|
||||||
|
} from "@app/app/[orgId]/settings/resources/public/ProxyResourceTargetsForm";
|
||||||
|
import {
|
||||||
|
use,
|
||||||
|
} from "react";
|
||||||
|
|
||||||
|
export default function ReverseProxyTargetsPage(props: {
|
||||||
|
params: Promise<{ resourceId: number; orgId: string }>;
|
||||||
|
}) {
|
||||||
|
const params = use(props.params);
|
||||||
|
const { resource, updateResource } = useResourceContext();
|
||||||
|
|
||||||
|
const { data: remoteTargets = [], isLoading: isLoadingTargets } = useQuery(
|
||||||
|
resourceQueries.resourceTargets({
|
||||||
|
resourceId: resource.resourceId
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isLoadingTargets) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsContainer>
|
||||||
|
<ProxyResourceTargetsForm
|
||||||
|
orgId={params.orgId}
|
||||||
|
isHttp={["http", "ssh", "rdp", "vnc"].includes(resource.mode)}
|
||||||
|
initialTargets={remoteTargets}
|
||||||
|
resource={resource}
|
||||||
|
updateResource={updateResource}
|
||||||
|
/>
|
||||||
|
</SettingsContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user