mirror of
https://github.com/fosrl/pangolin.git
synced 2026-07-09 07:30:40 +02:00
migrate private resources to page
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import type { SiteResourceData } from "@app/lib/privateResourceForm";
|
||||
import type { ListAllSiteResourcesByOrgResponse } from "@server/routers/siteResource";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { internal } from "@app/lib/api";
|
||||
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||
|
||||
export async function fetchSiteResourceByNiceId(
|
||||
orgId: string,
|
||||
niceId: string
|
||||
): Promise<SiteResourceData | null> {
|
||||
const res = await internal.get<
|
||||
AxiosResponse<ListAllSiteResourcesByOrgResponse>
|
||||
>(
|
||||
`/org/${orgId}/site-resources?query=${encodeURIComponent(niceId)}&pageSize=50`,
|
||||
await authCookieHeader()
|
||||
);
|
||||
|
||||
const match = res.data.data.siteResources.find((r) => r.niceId === niceId);
|
||||
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
id: match.siteResourceId,
|
||||
name: match.name,
|
||||
orgId,
|
||||
sites: match.siteIds.map((siteId, idx) => ({
|
||||
siteId,
|
||||
siteName: match.siteNames[idx],
|
||||
siteNiceId: match.siteNiceIds[idx],
|
||||
online: match.siteOnlines[idx]
|
||||
})),
|
||||
mode: match.mode,
|
||||
scheme: match.scheme,
|
||||
ssl: match.ssl,
|
||||
siteNames: match.siteNames,
|
||||
siteAddresses: match.siteAddresses || null,
|
||||
siteIds: match.siteIds,
|
||||
destination: match.destination,
|
||||
destinationPort: match.destinationPort ?? null,
|
||||
alias: match.alias || null,
|
||||
aliasAddress: match.aliasAddress || null,
|
||||
siteNiceIds: match.siteNiceIds,
|
||||
niceId: match.niceId,
|
||||
tcpPortRangeString: match.tcpPortRangeString || null,
|
||||
udpPortRangeString: match.udpPortRangeString || null,
|
||||
disableIcmp: match.disableIcmp || false,
|
||||
authDaemonMode: match.authDaemonMode ?? null,
|
||||
authDaemonPort: match.authDaemonPort ?? null,
|
||||
pamMode: match.pamMode ?? null,
|
||||
subdomain: match.subdomain ?? null,
|
||||
domainId: match.domainId ?? null,
|
||||
fullDomain: match.fullDomain ?? null,
|
||||
labels: match.labels ?? [],
|
||||
enabled: match.enabled
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,12 @@
|
||||
import type { LauncherResource } from "@server/routers/launcher/types";
|
||||
|
||||
export function getPrivateResourceSettingsHref(
|
||||
orgId: string,
|
||||
niceId: string
|
||||
): string {
|
||||
return `/${orgId}/settings/resources/private/${niceId}/general`;
|
||||
}
|
||||
|
||||
export function getLauncherResourceAdminHref(
|
||||
orgId: string,
|
||||
resource: LauncherResource
|
||||
@@ -8,10 +15,5 @@ export function getLauncherResourceAdminHref(
|
||||
return `/${orgId}/settings/resources/public/${resource.niceId}/general`;
|
||||
}
|
||||
|
||||
const qs = new URLSearchParams({ query: resource.niceId });
|
||||
if (resource.site?.siteId != null) {
|
||||
qs.set("siteId", String(resource.site.siteId));
|
||||
}
|
||||
|
||||
return `/${orgId}/settings/resources/private?${qs.toString()}`;
|
||||
return getPrivateResourceSettingsHref(orgId, resource.niceId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,656 @@
|
||||
import { z } from "zod";
|
||||
import type { InternalResourceRow } from "@app/components/PrivateResourcesTable";
|
||||
|
||||
export type PrivateResourceMode = "host" | "cidr" | "http" | "ssh";
|
||||
|
||||
export type SiteResourceData = InternalResourceRow & {
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
export type PortMode = "all" | "blocked" | "custom";
|
||||
|
||||
const tagSchema = z.object({ id: z.string(), text: z.string() });
|
||||
|
||||
export type PrivateResourceAccessTag = z.infer<typeof tagSchema>;
|
||||
|
||||
export type PrivateResourceClient = {
|
||||
clientId: number;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type PrivateResourceFormValues = {
|
||||
name: string;
|
||||
siteIds: number[];
|
||||
mode: PrivateResourceMode;
|
||||
destination: string | null;
|
||||
alias?: string | null;
|
||||
niceId?: string;
|
||||
enabled?: boolean;
|
||||
tcpPortRangeString?: string | null;
|
||||
udpPortRangeString?: string | null;
|
||||
disableIcmp?: boolean;
|
||||
authDaemonMode?: "site" | "remote" | "native" | null;
|
||||
authDaemonPort?: number | null;
|
||||
pamMode?: "passthrough" | "push" | null;
|
||||
destinationPort?: number | null;
|
||||
scheme?: "http" | "https";
|
||||
ssl?: boolean;
|
||||
httpConfigSubdomain?: string | null;
|
||||
httpConfigDomainId?: string | null;
|
||||
httpConfigFullDomain?: string | null;
|
||||
roles?: PrivateResourceAccessTag[];
|
||||
users?: PrivateResourceAccessTag[];
|
||||
clients?: PrivateResourceClient[];
|
||||
};
|
||||
|
||||
export type SiteResourceAccess = {
|
||||
roleIds: number[];
|
||||
userIds: string[];
|
||||
clientIds: number[];
|
||||
};
|
||||
|
||||
type TranslateFn = (key: string) => string;
|
||||
|
||||
export const isValidPortRangeString = (
|
||||
val: string | undefined | null
|
||||
): boolean => {
|
||||
if (!val || val.trim() === "" || val.trim() === "*") return true;
|
||||
const parts = val.split(",").map((p) => p.trim());
|
||||
for (const part of parts) {
|
||||
if (part === "") return false;
|
||||
if (part.includes("-")) {
|
||||
const [start, end] = part.split("-").map((p) => p.trim());
|
||||
if (!start || !end) return false;
|
||||
const startPort = parseInt(start, 10);
|
||||
const endPort = parseInt(end, 10);
|
||||
if (isNaN(startPort) || isNaN(endPort)) return false;
|
||||
if (
|
||||
startPort < 1 ||
|
||||
startPort > 65535 ||
|
||||
endPort < 1 ||
|
||||
endPort > 65535
|
||||
)
|
||||
return false;
|
||||
if (startPort > endPort) return false;
|
||||
} else {
|
||||
const port = parseInt(part, 10);
|
||||
if (isNaN(port) || port < 1 || port > 65535) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const getPortRangeValidationMessage = (t: TranslateFn) =>
|
||||
t("editInternalResourceDialogPortRangeValidationError");
|
||||
|
||||
export const createPortRangeStringSchema = (t: TranslateFn) =>
|
||||
z
|
||||
.string()
|
||||
.optional()
|
||||
.nullable()
|
||||
.refine((val) => isValidPortRangeString(val), {
|
||||
message: getPortRangeValidationMessage(t)
|
||||
});
|
||||
|
||||
export const getPortModeFromString = (
|
||||
val: string | undefined | null
|
||||
): PortMode => {
|
||||
if (val === "*") return "all";
|
||||
if (!val || val.trim() === "") return "blocked";
|
||||
return "custom";
|
||||
};
|
||||
|
||||
export const getPortStringFromMode = (
|
||||
mode: PortMode,
|
||||
customValue: string
|
||||
): string | undefined => {
|
||||
if (mode === "all") return "*";
|
||||
if (mode === "blocked") return "";
|
||||
return customValue;
|
||||
};
|
||||
|
||||
export const isHostname = (destination: string | null): boolean =>
|
||||
!!destination && /[a-zA-Z]/.test(destination);
|
||||
|
||||
export const cleanForFQDN = (name: string): string =>
|
||||
name
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9.-]/g, "-")
|
||||
.replace(/[-]+/g, "-")
|
||||
.replace(/^-|-$/g, "")
|
||||
.replace(/^\.|\.$/g, "");
|
||||
|
||||
export function applyAliasAutoGeneration(
|
||||
values: PrivateResourceFormValues
|
||||
): PrivateResourceFormValues {
|
||||
const data = { ...values };
|
||||
if (
|
||||
(data.mode === "host" ||
|
||||
data.mode === "http" ||
|
||||
(data.mode === "ssh" && data.authDaemonMode !== "native")) &&
|
||||
isHostname(data.destination)
|
||||
) {
|
||||
const currentAlias = data.alias?.trim() || "";
|
||||
if (!currentAlias) {
|
||||
let aliasValue = data.destination!;
|
||||
if (data.destination?.toLowerCase() === "localhost") {
|
||||
aliasValue = `${cleanForFQDN(data.name)}.internal`;
|
||||
}
|
||||
data.alias = aliasValue;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
export function accessTagsToIds(access: {
|
||||
roles?: PrivateResourceAccessTag[];
|
||||
users?: PrivateResourceAccessTag[];
|
||||
clients?: PrivateResourceClient[];
|
||||
}): SiteResourceAccess {
|
||||
return {
|
||||
roleIds: (access.roles ?? []).map((r) => parseInt(r.id)),
|
||||
userIds: (access.users ?? []).map((u) => u.id),
|
||||
clientIds: (access.clients ?? []).map((c) => c.clientId)
|
||||
};
|
||||
}
|
||||
|
||||
export function buildCreateSiteResourcePayload(
|
||||
values: PrivateResourceFormValues
|
||||
) {
|
||||
const data = applyAliasAutoGeneration(values);
|
||||
const isNativeSsh = data.mode === "ssh" && data.authDaemonMode === "native";
|
||||
|
||||
return {
|
||||
name: data.name,
|
||||
siteIds: data.siteIds,
|
||||
mode: data.mode,
|
||||
destination: isNativeSsh ? undefined : (data.destination ?? undefined),
|
||||
enabled: true,
|
||||
...(data.mode === "http" && {
|
||||
scheme: data.scheme,
|
||||
ssl: data.ssl ?? false,
|
||||
destinationPort: data.destinationPort ?? undefined,
|
||||
domainId: data.httpConfigDomainId
|
||||
? data.httpConfigDomainId
|
||||
: undefined,
|
||||
subdomain: data.httpConfigSubdomain
|
||||
? data.httpConfigSubdomain
|
||||
: undefined
|
||||
}),
|
||||
...(data.mode === "host" && {
|
||||
alias:
|
||||
data.alias &&
|
||||
typeof data.alias === "string" &&
|
||||
data.alias.trim()
|
||||
? data.alias
|
||||
: undefined,
|
||||
...(data.authDaemonMode != null && {
|
||||
authDaemonMode: data.authDaemonMode
|
||||
}),
|
||||
...(data.authDaemonMode === "remote" &&
|
||||
data.authDaemonPort != null && {
|
||||
authDaemonPort: data.authDaemonPort
|
||||
})
|
||||
}),
|
||||
...(data.mode === "ssh" && {
|
||||
alias:
|
||||
data.alias &&
|
||||
typeof data.alias === "string" &&
|
||||
data.alias.trim()
|
||||
? data.alias
|
||||
: undefined,
|
||||
...(!isNativeSsh && {
|
||||
destinationPort: data.destinationPort ?? undefined
|
||||
}),
|
||||
pamMode: data.pamMode ?? undefined,
|
||||
...(data.authDaemonMode != null && {
|
||||
authDaemonMode: data.authDaemonMode
|
||||
}),
|
||||
...(data.authDaemonMode === "remote" &&
|
||||
data.authDaemonPort != null && {
|
||||
authDaemonPort: data.authDaemonPort
|
||||
})
|
||||
}),
|
||||
...((data.mode === "host" || data.mode === "cidr") && {
|
||||
tcpPortRangeString: data.tcpPortRangeString,
|
||||
udpPortRangeString: data.udpPortRangeString,
|
||||
disableIcmp: data.disableIcmp ?? false
|
||||
}),
|
||||
roleIds: data.roles ? data.roles.map((r) => parseInt(r.id)) : [],
|
||||
userIds: data.users ? data.users.map((u) => u.id) : [],
|
||||
clientIds: data.clients ? data.clients.map((c) => c.clientId) : []
|
||||
};
|
||||
}
|
||||
|
||||
export function buildUpdateSiteResourcePayload(
|
||||
values: PrivateResourceFormValues,
|
||||
access: SiteResourceAccess
|
||||
) {
|
||||
const data = applyAliasAutoGeneration(values);
|
||||
const isNativeSsh = data.mode === "ssh" && data.authDaemonMode === "native";
|
||||
|
||||
return {
|
||||
name: data.name,
|
||||
siteIds: data.siteIds,
|
||||
mode: data.mode,
|
||||
niceId: data.niceId,
|
||||
enabled: data.enabled,
|
||||
...(isNativeSsh
|
||||
? { destination: null, destinationPort: null }
|
||||
: { destination: data.destination ?? undefined }),
|
||||
...(data.mode === "http" && {
|
||||
scheme: data.scheme,
|
||||
ssl: data.ssl ?? false,
|
||||
destinationPort: data.destinationPort ?? null,
|
||||
domainId: data.httpConfigDomainId
|
||||
? data.httpConfigDomainId
|
||||
: undefined,
|
||||
subdomain: data.httpConfigSubdomain
|
||||
? data.httpConfigSubdomain
|
||||
: undefined
|
||||
}),
|
||||
...(data.mode === "host" && {
|
||||
alias:
|
||||
data.alias &&
|
||||
typeof data.alias === "string" &&
|
||||
data.alias.trim()
|
||||
? data.alias
|
||||
: null,
|
||||
...(data.authDaemonMode != null && {
|
||||
authDaemonMode: data.authDaemonMode
|
||||
}),
|
||||
...(data.authDaemonMode === "remote" && {
|
||||
authDaemonPort: data.authDaemonPort || null
|
||||
})
|
||||
}),
|
||||
...(data.mode === "ssh" && {
|
||||
alias:
|
||||
data.alias &&
|
||||
typeof data.alias === "string" &&
|
||||
data.alias.trim()
|
||||
? data.alias
|
||||
: null,
|
||||
...(!isNativeSsh && {
|
||||
destinationPort: data.destinationPort ?? null
|
||||
}),
|
||||
pamMode: data.pamMode ?? undefined,
|
||||
...(data.authDaemonMode != null && {
|
||||
authDaemonMode: data.authDaemonMode
|
||||
}),
|
||||
...(data.authDaemonMode === "remote" && {
|
||||
authDaemonPort: data.authDaemonPort || null
|
||||
})
|
||||
}),
|
||||
...((data.mode === "host" || data.mode === "cidr") && {
|
||||
tcpPortRangeString: data.tcpPortRangeString,
|
||||
udpPortRangeString: data.udpPortRangeString,
|
||||
disableIcmp: data.disableIcmp ?? false
|
||||
}),
|
||||
roleIds: access.roleIds,
|
||||
userIds: access.userIds,
|
||||
clientIds: access.clientIds
|
||||
};
|
||||
}
|
||||
|
||||
export function inferSshPamMode(
|
||||
authDaemonMode?: string | null,
|
||||
pamMode?: "passthrough" | "push" | null
|
||||
): "passthrough" | "push" {
|
||||
if (pamMode === "passthrough" || pamMode === "push") {
|
||||
return pamMode;
|
||||
}
|
||||
|
||||
return authDaemonMode === "remote" ? "push" : "passthrough";
|
||||
}
|
||||
|
||||
export function siteResourceToFormValues(
|
||||
resource: SiteResourceData
|
||||
): PrivateResourceFormValues {
|
||||
return {
|
||||
name: resource.name,
|
||||
siteIds: resource.siteIds,
|
||||
mode: resource.mode ?? "host",
|
||||
destination: resource.destination ?? "",
|
||||
alias: resource.alias ?? null,
|
||||
destinationPort: resource.destinationPort ?? null,
|
||||
scheme: resource.scheme ?? "http",
|
||||
ssl: resource.ssl ?? false,
|
||||
httpConfigSubdomain: resource.subdomain ?? null,
|
||||
httpConfigDomainId: resource.domainId ?? null,
|
||||
httpConfigFullDomain: resource.fullDomain ?? null,
|
||||
tcpPortRangeString: resource.tcpPortRangeString ?? "*",
|
||||
udpPortRangeString: resource.udpPortRangeString ?? "*",
|
||||
disableIcmp: resource.disableIcmp ?? false,
|
||||
authDaemonMode:
|
||||
resource.authDaemonMode === "native"
|
||||
? "native"
|
||||
: (resource.authDaemonMode ?? "site"),
|
||||
authDaemonPort: resource.authDaemonPort ?? null,
|
||||
pamMode: inferSshPamMode(resource.authDaemonMode, resource.pamMode),
|
||||
niceId: resource.niceId,
|
||||
enabled: resource.enabled
|
||||
};
|
||||
}
|
||||
|
||||
export function createGeneralFormSchema(t: TranslateFn) {
|
||||
return z.object({
|
||||
name: z
|
||||
.string()
|
||||
.min(1, t("editInternalResourceDialogNameRequired"))
|
||||
.max(255, t("editInternalResourceDialogNameMaxLength")),
|
||||
niceId: z
|
||||
.string()
|
||||
.min(1)
|
||||
.max(255)
|
||||
.regex(/^[a-zA-Z0-9-]+$/)
|
||||
});
|
||||
}
|
||||
|
||||
export function createAccessFormSchema() {
|
||||
return z.object({
|
||||
roles: z.array(tagSchema).optional(),
|
||||
users: z.array(tagSchema).optional(),
|
||||
clients: z
|
||||
.array(
|
||||
z.object({
|
||||
clientId: z.number(),
|
||||
name: z.string()
|
||||
})
|
||||
)
|
||||
.optional()
|
||||
});
|
||||
}
|
||||
|
||||
export function createCreateFormSchema(t: TranslateFn) {
|
||||
const destinationRequired = t(
|
||||
"createInternalResourceDialogDestinationRequired"
|
||||
);
|
||||
|
||||
return z
|
||||
.object({
|
||||
name: z
|
||||
.string()
|
||||
.min(1, t("createInternalResourceDialogNameRequired"))
|
||||
.max(255, t("createInternalResourceDialogNameMaxLength")),
|
||||
siteIds: z
|
||||
.array(z.number().int().positive())
|
||||
.min(1, t("createInternalResourceDialogPleaseSelectSite")),
|
||||
mode: z.enum(["host", "cidr", "http", "ssh"]),
|
||||
destination: z.string().nullish(),
|
||||
alias: z.string().nullish(),
|
||||
destinationPort: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(65535)
|
||||
.optional()
|
||||
.nullable(),
|
||||
scheme: z.enum(["http", "https"]).optional(),
|
||||
ssl: z.boolean().optional(),
|
||||
httpConfigSubdomain: z.string().nullish(),
|
||||
httpConfigDomainId: z.string().nullish(),
|
||||
httpConfigFullDomain: z.string().nullish(),
|
||||
authDaemonMode: z
|
||||
.enum(["site", "remote", "native"])
|
||||
.optional()
|
||||
.nullable(),
|
||||
standardDaemonLocation: z
|
||||
.enum(["site", "remote"])
|
||||
.optional()
|
||||
.nullable(),
|
||||
authDaemonPort: z.number().int().positive().optional().nullable(),
|
||||
pamMode: z.enum(["passthrough", "push"]).optional().nullable(),
|
||||
disableIcmp: z.boolean().optional()
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
const isNativeSsh =
|
||||
data.mode === "ssh" && data.authDaemonMode === "native";
|
||||
const trimmedDestination = data.destination?.trim();
|
||||
if (
|
||||
data.mode !== "ssh" &&
|
||||
(!trimmedDestination || trimmedDestination.length < 1)
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: destinationRequired,
|
||||
path: ["destination"]
|
||||
});
|
||||
}
|
||||
if (data.mode === "ssh" && !isNativeSsh) {
|
||||
if (!trimmedDestination || trimmedDestination.length < 1) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: destinationRequired,
|
||||
path: ["destination"]
|
||||
});
|
||||
}
|
||||
if (
|
||||
data.destinationPort == null ||
|
||||
!Number.isFinite(data.destinationPort) ||
|
||||
data.destinationPort < 1
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: t("internalResourceHttpPortRequired"),
|
||||
path: ["destinationPort"]
|
||||
});
|
||||
}
|
||||
}
|
||||
if (data.mode === "http") {
|
||||
if (!data.scheme) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: t("internalResourceDownstreamSchemeRequired"),
|
||||
path: ["scheme"]
|
||||
});
|
||||
}
|
||||
if (
|
||||
data.destinationPort == null ||
|
||||
!Number.isFinite(data.destinationPort) ||
|
||||
data.destinationPort < 1
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: t("internalResourceHttpPortRequired"),
|
||||
path: ["destinationPort"]
|
||||
});
|
||||
}
|
||||
if (!data.httpConfigDomainId) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: t("domainRequired"),
|
||||
path: ["httpConfigDomainId"]
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function destinationRefine(
|
||||
data: {
|
||||
mode: PrivateResourceMode;
|
||||
destination?: string | null;
|
||||
authDaemonMode?: string | null;
|
||||
destinationPort?: number | null;
|
||||
scheme?: string;
|
||||
httpConfigDomainId?: string | null;
|
||||
},
|
||||
ctx: z.RefinementCtx,
|
||||
t: TranslateFn,
|
||||
destinationRequired?: string
|
||||
) {
|
||||
const isNativeSsh = data.mode === "ssh" && data.authDaemonMode === "native";
|
||||
const trimmedDestination = data.destination?.trim();
|
||||
if (
|
||||
!isNativeSsh &&
|
||||
(!trimmedDestination || trimmedDestination.length < 1)
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: destinationRequired ?? "Destination is required",
|
||||
path: ["destination"]
|
||||
});
|
||||
}
|
||||
if (data.mode === "ssh" && !isNativeSsh) {
|
||||
if (
|
||||
data.destinationPort == null ||
|
||||
!Number.isFinite(data.destinationPort) ||
|
||||
data.destinationPort < 1
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: t("internalResourceHttpPortRequired"),
|
||||
path: ["destinationPort"]
|
||||
});
|
||||
}
|
||||
}
|
||||
if (data.mode === "http") {
|
||||
if (!data.scheme) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: t("internalResourceDownstreamSchemeRequired"),
|
||||
path: ["scheme"]
|
||||
});
|
||||
}
|
||||
if (
|
||||
data.destinationPort == null ||
|
||||
!Number.isFinite(data.destinationPort) ||
|
||||
data.destinationPort < 1
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: t("internalResourceHttpPortRequired"),
|
||||
path: ["destinationPort"]
|
||||
});
|
||||
}
|
||||
if (!data.httpConfigDomainId) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: t("domainRequired"),
|
||||
path: ["httpConfigDomainId"]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createHostFormSchema(t: TranslateFn) {
|
||||
return z
|
||||
.object({
|
||||
siteIds: z.array(z.number().int().positive()).min(1),
|
||||
mode: z.literal("host"),
|
||||
destination: z.string().nullish(),
|
||||
alias: z.string().nullish(),
|
||||
tcpPortRangeString: createPortRangeStringSchema(t),
|
||||
udpPortRangeString: createPortRangeStringSchema(t),
|
||||
disableIcmp: z.boolean().optional(),
|
||||
authDaemonMode: z
|
||||
.enum(["site", "remote", "native"])
|
||||
.optional()
|
||||
.nullable(),
|
||||
authDaemonPort: z.number().int().positive().optional().nullable()
|
||||
})
|
||||
.superRefine((data, ctx) => destinationRefine(data, ctx, t));
|
||||
}
|
||||
|
||||
export function createCidrFormSchema(t: TranslateFn) {
|
||||
return z
|
||||
.object({
|
||||
siteIds: z.array(z.number().int().positive()).min(1),
|
||||
mode: z.literal("cidr"),
|
||||
destination: z.string().nullish(),
|
||||
tcpPortRangeString: createPortRangeStringSchema(t),
|
||||
udpPortRangeString: createPortRangeStringSchema(t),
|
||||
disableIcmp: z.boolean().optional()
|
||||
})
|
||||
.superRefine((data, ctx) => destinationRefine(data, ctx, t));
|
||||
}
|
||||
|
||||
export function createHttpFormSchema(t: TranslateFn) {
|
||||
return z
|
||||
.object({
|
||||
siteIds: z.array(z.number().int().positive()).min(1),
|
||||
mode: z.literal("http"),
|
||||
destination: z.string().nullish(),
|
||||
destinationPort: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(65535)
|
||||
.optional()
|
||||
.nullable(),
|
||||
scheme: z.enum(["http", "https"]).optional(),
|
||||
ssl: z.boolean().optional(),
|
||||
httpConfigSubdomain: z.string().nullish(),
|
||||
httpConfigDomainId: z.string().nullish(),
|
||||
httpConfigFullDomain: z.string().nullish()
|
||||
})
|
||||
.superRefine((data, ctx) => destinationRefine(data, ctx, t));
|
||||
}
|
||||
|
||||
export function createSshFormSchema(
|
||||
t: TranslateFn,
|
||||
options?: { isNative?: boolean }
|
||||
) {
|
||||
const isNative = options?.isNative ?? false;
|
||||
|
||||
return z
|
||||
.object({
|
||||
siteIds: z.array(z.number().int().positive()).min(1),
|
||||
mode: z.literal("ssh"),
|
||||
destination: z.string().nullish(),
|
||||
alias: z.string().nullish(),
|
||||
destinationPort: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(65535)
|
||||
.optional()
|
||||
.nullable(),
|
||||
pamMode: z.enum(["passthrough", "push"]),
|
||||
standardDaemonLocation: z.enum(["site", "remote"]),
|
||||
authDaemonPort: z.string()
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
destinationRefine(
|
||||
{
|
||||
...data,
|
||||
authDaemonMode: isNative
|
||||
? "native"
|
||||
: data.standardDaemonLocation
|
||||
},
|
||||
ctx,
|
||||
t
|
||||
);
|
||||
|
||||
const showDaemonPort =
|
||||
!isNative &&
|
||||
data.pamMode === "push" &&
|
||||
data.standardDaemonLocation === "remote";
|
||||
|
||||
if (showDaemonPort) {
|
||||
const port = Number(data.authDaemonPort);
|
||||
if (
|
||||
!data.authDaemonPort.trim() ||
|
||||
!Number.isInteger(port) ||
|
||||
port < 1 ||
|
||||
port > 65535
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: t("healthCheckPortInvalid"),
|
||||
path: ["authDaemonPort"]
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function mergeFormValuesWithResource(
|
||||
resource: SiteResourceData,
|
||||
partial: Partial<PrivateResourceFormValues>
|
||||
): PrivateResourceFormValues {
|
||||
return {
|
||||
...siteResourceToFormValues(resource),
|
||||
...partial
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user