mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-28 11:43:03 +00:00
Trying to get these forms to work
This commit is contained in:
@@ -346,7 +346,7 @@ export const siteResources = pgTable("siteResources", {
|
||||
niceId: varchar("niceId").notNull(),
|
||||
name: varchar("name").notNull(),
|
||||
ssl: boolean("ssl").notNull().default(false),
|
||||
mode: varchar("mode").$type<"host" | "cidr" | "http">().notNull(), // "host" | "cidr" | "http"
|
||||
mode: varchar("mode").$type<"host" | "cidr" | "http" | "ssh">().notNull(), // "host" | "cidr" | "http"
|
||||
scheme: varchar("scheme").$type<"http" | "https">(), // only for when we are doing https or http mode
|
||||
proxyPort: integer("proxyPort"), // only for port mode
|
||||
destinationPort: integer("destinationPort"), // only for port mode
|
||||
|
||||
@@ -380,7 +380,7 @@ export const siteResources = sqliteTable("siteResources", {
|
||||
niceId: text("niceId").notNull(),
|
||||
name: text("name").notNull(),
|
||||
ssl: integer("ssl", { mode: "boolean" }).notNull().default(false),
|
||||
mode: text("mode").$type<"host" | "cidr" | "http">().notNull(), // "host" | "cidr" | "http"
|
||||
mode: text("mode").$type<"host" | "cidr" | "http" | "ssh">().notNull(), // "host" | "cidr" | "http"
|
||||
scheme: text("scheme").$type<"http" | "https">(), // only for when we are doing https or http mode
|
||||
proxyPort: integer("proxyPort"), // only for port mode
|
||||
destinationPort: integer("destinationPort"), // only for port mode
|
||||
|
||||
@@ -44,7 +44,7 @@ const createSiteResourceSchema = z
|
||||
name: z.string().min(1).max(255),
|
||||
niceId: z.string().optional(),
|
||||
// protocol: z.enum(["tcp", "udp"]).optional(),
|
||||
mode: z.enum(["host", "cidr", "http"]),
|
||||
mode: z.enum(["host", "cidr", "http", "ssh"]),
|
||||
ssl: z.boolean().optional(), // only used for http mode
|
||||
scheme: z.enum(["http", "https"]).optional(),
|
||||
siteIds: z.array(z.int()).optional(),
|
||||
@@ -75,7 +75,7 @@ const createSiteResourceSchema = z
|
||||
.strict()
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.mode === "host") {
|
||||
if (data.mode === "host" || data.mode === "ssh") {
|
||||
// Check if it's a valid IP address using zod (v4 or v6)
|
||||
const isValidIP = z
|
||||
// .union([z.ipv4(), z.ipv6()])
|
||||
@@ -117,13 +117,24 @@ const createSiteResourceSchema = z
|
||||
)
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.mode !== "http") return true;
|
||||
if (data.mode === "http") {
|
||||
return (
|
||||
data.scheme !== undefined &&
|
||||
data.scheme !== null &&
|
||||
data.destinationPort !== undefined &&
|
||||
data.destinationPort !== null &&
|
||||
data.destinationPort >= 1 &&
|
||||
data.destinationPort <= 65535
|
||||
);
|
||||
} else if (data.mode === "ssh") {
|
||||
// just check the destinationPort
|
||||
return (
|
||||
data.destinationPort === undefined ||
|
||||
(data.destinationPort !== null &&
|
||||
data.destinationPort >= 1 &&
|
||||
data.destinationPort <= 65535)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
message:
|
||||
@@ -391,6 +402,15 @@ export async function createSiteResource(
|
||||
);
|
||||
}
|
||||
|
||||
let tcpPortRangeStringAdjusted = tcpPortRangeString;
|
||||
if (mode === "http") {
|
||||
tcpPortRangeStringAdjusted = "443,80";
|
||||
} else if (mode === "ssh") {
|
||||
tcpPortRangeStringAdjusted = destinationPort
|
||||
? destinationPort.toString()
|
||||
: "22";
|
||||
}
|
||||
|
||||
// Create the site resource
|
||||
const insertValues: typeof siteResources.$inferInsert = {
|
||||
niceId: updatedNiceId!,
|
||||
@@ -405,10 +425,12 @@ export async function createSiteResource(
|
||||
enabled,
|
||||
alias: alias ? alias.trim() : null,
|
||||
aliasAddress,
|
||||
tcpPortRangeString:
|
||||
mode == "http" ? "443,80" : tcpPortRangeString,
|
||||
udpPortRangeString: mode == "http" ? "" : udpPortRangeString,
|
||||
disableIcmp: disableIcmp || (mode == "http" ? true : false), // default to true for http resources, otherwise false
|
||||
tcpPortRangeString: tcpPortRangeStringAdjusted,
|
||||
udpPortRangeString:
|
||||
mode == "http" || mode == "ssh" ? "" : udpPortRangeString,
|
||||
disableIcmp:
|
||||
disableIcmp ||
|
||||
(mode == "http" || mode == "ssh" ? true : false), // default to true for http resources, otherwise false
|
||||
domainId,
|
||||
subdomain: finalSubdomain,
|
||||
fullDomain
|
||||
|
||||
@@ -56,7 +56,7 @@ const updateSiteResourceSchema = z
|
||||
)
|
||||
.optional(),
|
||||
// mode: z.enum(["host", "cidr", "port"]).optional(),
|
||||
mode: z.enum(["host", "cidr", "http"]).optional(),
|
||||
mode: z.enum(["host", "cidr", "http", "ssh"]).optional(),
|
||||
ssl: z.boolean().optional(),
|
||||
scheme: z.enum(["http", "https"]).nullish(),
|
||||
// proxyPort: z.int().positive().nullish(),
|
||||
@@ -85,7 +85,10 @@ const updateSiteResourceSchema = z
|
||||
.strict()
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.mode === "host" && data.destination) {
|
||||
if (
|
||||
(data.mode === "host" || data.mode == "ssh") &&
|
||||
data.destination
|
||||
) {
|
||||
const isValidIP = z
|
||||
// .union([z.ipv4(), z.ipv6()])
|
||||
.union([z.ipv4()]) // for now lets just do ipv4 until we verify ipv6 works everywhere
|
||||
@@ -126,7 +129,7 @@ const updateSiteResourceSchema = z
|
||||
)
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.mode !== "http") return true;
|
||||
if (data.mode === "http") {
|
||||
return (
|
||||
data.scheme !== undefined &&
|
||||
data.scheme !== null &&
|
||||
@@ -135,6 +138,15 @@ const updateSiteResourceSchema = z
|
||||
data.destinationPort >= 1 &&
|
||||
data.destinationPort <= 65535
|
||||
);
|
||||
} else if (data.mode === "ssh") {
|
||||
// just check the destinationPort
|
||||
return (
|
||||
data.destinationPort === undefined ||
|
||||
(data.destinationPort !== null &&
|
||||
data.destinationPort >= 1 &&
|
||||
data.destinationPort <= 65535)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
message:
|
||||
@@ -446,6 +458,16 @@ export async function updateSiteResource(
|
||||
})
|
||||
}
|
||||
: {};
|
||||
|
||||
let tcpPortRangeStringAdjusted = tcpPortRangeString;
|
||||
if (mode === "http") {
|
||||
tcpPortRangeStringAdjusted = "443,80";
|
||||
} else if (mode === "ssh") {
|
||||
tcpPortRangeStringAdjusted = destinationPort
|
||||
? destinationPort.toString()
|
||||
: "22";
|
||||
}
|
||||
|
||||
[updatedSiteResource] = await trx
|
||||
.update(siteResources)
|
||||
.set({
|
||||
@@ -458,12 +480,14 @@ export async function updateSiteResource(
|
||||
destinationPort,
|
||||
enabled,
|
||||
alias: alias ? alias.trim() : null,
|
||||
tcpPortRangeString:
|
||||
mode == "http" ? "443,80" : tcpPortRangeString,
|
||||
tcpPortRangeString: tcpPortRangeStringAdjusted,
|
||||
udpPortRangeString:
|
||||
mode == "http" ? "" : udpPortRangeString,
|
||||
mode == "http" || mode == "ssh"
|
||||
? ""
|
||||
: udpPortRangeString,
|
||||
disableIcmp:
|
||||
disableIcmp || (mode == "http" ? true : false), // default to true for http resources, otherwise false
|
||||
disableIcmp ||
|
||||
(mode == "http" || mode == "ssh" ? true : false), // default to true for http resources, otherwise false
|
||||
domainId,
|
||||
subdomain: finalSubdomain,
|
||||
fullDomain,
|
||||
|
||||
@@ -120,7 +120,7 @@ export default async function ClientResourcesPage(
|
||||
// proxyPort: siteResource.proxyPort,
|
||||
siteIds: siteResource.siteIds,
|
||||
destination: siteResource.destination,
|
||||
httpHttpsPort: siteResource.destinationPort ?? null,
|
||||
destinationPort: siteResource.destinationPort ?? null,
|
||||
alias: siteResource.alias || null,
|
||||
aliasAddress: siteResource.aliasAddress || null,
|
||||
siteNiceIds: siteResource.siteNiceIds,
|
||||
|
||||
@@ -83,7 +83,7 @@ export type InternalResourceRow = {
|
||||
// protocol: string | null;
|
||||
// proxyPort: number | null;
|
||||
destination: string;
|
||||
httpHttpsPort: number | null;
|
||||
destinationPort: number | null;
|
||||
alias: string | null;
|
||||
aliasAddress: string | null;
|
||||
niceId: string;
|
||||
@@ -107,7 +107,7 @@ function formatDestinationDisplay(row: InternalResourceRow): string {
|
||||
return formatSiteResourceDestinationDisplay({
|
||||
mode: row.mode,
|
||||
destination: row.destination,
|
||||
httpHttpsPort: row.httpHttpsPort,
|
||||
destinationPort: row.destinationPort,
|
||||
scheme: row.scheme
|
||||
});
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ export default function CreateInternalResourceDialog({
|
||||
...(data.mode === "http" && {
|
||||
scheme: data.scheme,
|
||||
ssl: data.ssl ?? false,
|
||||
destinationPort: data.httpHttpsPort ?? undefined,
|
||||
destinationPort: data.destinationPort ?? undefined,
|
||||
domainId: data.httpConfigDomainId
|
||||
? data.httpConfigDomainId
|
||||
: undefined,
|
||||
|
||||
@@ -78,7 +78,7 @@ export default function EditInternalResourceDialog({
|
||||
...(data.mode === "http" && {
|
||||
scheme: data.scheme,
|
||||
ssl: data.ssl ?? false,
|
||||
destinationPort: data.httpHttpsPort ?? null,
|
||||
destinationPort: data.destinationPort ?? null,
|
||||
domainId: data.httpConfigDomainId
|
||||
? data.httpConfigDomainId
|
||||
: undefined,
|
||||
|
||||
@@ -54,6 +54,7 @@ import {
|
||||
MultiSitesSelector,
|
||||
formatMultiSitesSelectorLabel
|
||||
} from "./multi-site-selector";
|
||||
import { SitesSelector } from "./site-selector";
|
||||
import type { Selectedsite } from "./site-selector";
|
||||
|
||||
import { MachinesSelector } from "./machines-selector";
|
||||
@@ -154,7 +155,7 @@ export type InternalResourceData = {
|
||||
authDaemonMode?: "site" | "remote" | "native" | null;
|
||||
authDaemonPort?: number | null;
|
||||
pamMode?: "passthrough" | "push" | null;
|
||||
httpHttpsPort?: number | null;
|
||||
destinationPort?: number | null;
|
||||
scheme?: "http" | "https" | null;
|
||||
ssl?: boolean;
|
||||
subdomain?: string | null;
|
||||
@@ -187,7 +188,7 @@ export type InternalResourceFormValues = {
|
||||
authDaemonMode?: "site" | "remote" | "native" | null;
|
||||
authDaemonPort?: number | null;
|
||||
pamMode?: "passthrough" | "push" | null;
|
||||
httpHttpsPort?: number | null;
|
||||
destinationPort?: number | null;
|
||||
scheme?: "http" | "https";
|
||||
ssl?: boolean;
|
||||
httpConfigSubdomain?: string | null;
|
||||
@@ -286,7 +287,7 @@ export function InternalResourceForm({
|
||||
variant === "create"
|
||||
? "createInternalResourceDialogAlias"
|
||||
: "editInternalResourceDialogAlias";
|
||||
const httpHttpsPortLabelKey =
|
||||
const destinationPortLabelKey =
|
||||
variant === "create"
|
||||
? "createInternalResourceDialogModePort"
|
||||
: "editInternalResourceDialogModePort";
|
||||
@@ -308,16 +309,9 @@ export function InternalResourceForm({
|
||||
name: z.string().min(1, t(nameRequiredKey)).max(255, t(nameMaxKey)),
|
||||
siteIds: siteIdsSchema,
|
||||
mode: z.enum(["host", "cidr", "http", "ssh"]),
|
||||
destination: z
|
||||
.string()
|
||||
.min(
|
||||
1,
|
||||
destinationRequiredKey
|
||||
? { message: t(destinationRequiredKey) }
|
||||
: undefined
|
||||
),
|
||||
destination: z.string(),
|
||||
alias: z.string().nullish(),
|
||||
httpHttpsPort: z
|
||||
destinationPort: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
@@ -356,6 +350,20 @@ export function InternalResourceForm({
|
||||
.optional()
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
const isNativeSsh =
|
||||
data.mode === "ssh" && data.authDaemonMode === "native";
|
||||
if (
|
||||
!isNativeSsh &&
|
||||
(!data.destination || data.destination.length < 1)
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: destinationRequiredKey
|
||||
? t(destinationRequiredKey)
|
||||
: "Destination is required",
|
||||
path: ["destination"]
|
||||
});
|
||||
}
|
||||
if (data.mode !== "http") return;
|
||||
if (!data.scheme) {
|
||||
ctx.addIssue({
|
||||
@@ -365,14 +373,15 @@ export function InternalResourceForm({
|
||||
});
|
||||
}
|
||||
if (
|
||||
data.httpHttpsPort == null ||
|
||||
!Number.isFinite(data.httpHttpsPort) ||
|
||||
data.httpHttpsPort < 1
|
||||
!isNativeSsh &&
|
||||
(data.destinationPort == null ||
|
||||
!Number.isFinite(data.destinationPort) ||
|
||||
data.destinationPort < 1)
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: t("internalResourceHttpPortRequired"),
|
||||
path: ["httpHttpsPort"]
|
||||
path: ["destinationPort"]
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -523,7 +532,7 @@ export function InternalResourceForm({
|
||||
: (resource.authDaemonMode ?? "site"),
|
||||
authDaemonPort: resource.authDaemonPort ?? null,
|
||||
pamMode: resource.pamMode ?? "passthrough",
|
||||
httpHttpsPort: resource.httpHttpsPort ?? null,
|
||||
destinationPort: resource.destinationPort ?? null,
|
||||
scheme: resource.scheme ?? "http",
|
||||
ssl: resource.ssl ?? false,
|
||||
httpConfigSubdomain: resource.subdomain ?? null,
|
||||
@@ -540,7 +549,7 @@ export function InternalResourceForm({
|
||||
mode: "host",
|
||||
destination: "",
|
||||
alias: null,
|
||||
httpHttpsPort: null,
|
||||
destinationPort: null,
|
||||
scheme: "http",
|
||||
ssl: true,
|
||||
httpConfigSubdomain: null,
|
||||
@@ -605,7 +614,7 @@ export function InternalResourceForm({
|
||||
mode: "host",
|
||||
destination: "",
|
||||
alias: null,
|
||||
httpHttpsPort: null,
|
||||
destinationPort: null,
|
||||
scheme: "http",
|
||||
ssl: true,
|
||||
httpConfigSubdomain: null,
|
||||
@@ -641,7 +650,7 @@ export function InternalResourceForm({
|
||||
mode: resource.mode ?? "host",
|
||||
destination: resource.destination ?? "",
|
||||
alias: resource.alias ?? null,
|
||||
httpHttpsPort: resource.httpHttpsPort ?? null,
|
||||
destinationPort: resource.destinationPort ?? null,
|
||||
scheme: resource.scheme ?? "http",
|
||||
ssl: resource.ssl ?? false,
|
||||
httpConfigSubdomain: resource.subdomain ?? null,
|
||||
@@ -812,8 +821,69 @@ export function InternalResourceForm({
|
||||
<FormLabel>
|
||||
{t("sites")}
|
||||
</FormLabel>
|
||||
{mode === "ssh" &&
|
||||
sshServerMode ===
|
||||
"native" ? (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<PopoverTrigger
|
||||
asChild
|
||||
>
|
||||
<FormControl>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
className={cn(
|
||||
"w-full justify-between",
|
||||
selectedSites.length ===
|
||||
0 &&
|
||||
"text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
<span className="truncate text-left">
|
||||
{selectedSites[0]
|
||||
?.name ??
|
||||
t(
|
||||
"selectSite"
|
||||
)}
|
||||
</span>
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</FormControl>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-full p-0">
|
||||
<SitesSelector
|
||||
orgId={
|
||||
orgId
|
||||
}
|
||||
selectedSite={
|
||||
selectedSites[0] ??
|
||||
null
|
||||
}
|
||||
filterTypes={[
|
||||
"newt"
|
||||
]}
|
||||
onSelectSite={(
|
||||
site
|
||||
) => {
|
||||
setSelectedSites(
|
||||
[
|
||||
site
|
||||
]
|
||||
);
|
||||
field.onChange(
|
||||
[
|
||||
site.siteId
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
) : (
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
asChild
|
||||
>
|
||||
<FormControl>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -837,7 +907,9 @@ export function InternalResourceForm({
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-full p-0">
|
||||
<MultiSitesSelector
|
||||
orgId={orgId}
|
||||
orgId={
|
||||
orgId
|
||||
}
|
||||
selectedSites={
|
||||
selectedSites
|
||||
}
|
||||
@@ -862,6 +934,7 @@ export function InternalResourceForm({
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)}
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
@@ -950,8 +1023,10 @@ export function InternalResourceForm({
|
||||
"grid gap-4 items-start",
|
||||
mode === "cidr" && "grid-cols-1",
|
||||
mode === "http" && "grid-cols-3",
|
||||
(mode === "host" || mode === "ssh") &&
|
||||
"grid-cols-2"
|
||||
mode === "host" && "grid-cols-2",
|
||||
mode === "ssh" &&
|
||||
sshServerMode !== "native" &&
|
||||
"grid-cols-3"
|
||||
)}
|
||||
>
|
||||
{mode === "http" && (
|
||||
@@ -996,7 +1071,11 @@ export function InternalResourceForm({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{sshServerMode !== "native" && (
|
||||
{((mode === "ssh" &&
|
||||
sshServerMode !== "native") ||
|
||||
mode === "http" ||
|
||||
mode === "host" ||
|
||||
mode === "cidr") && (
|
||||
<div
|
||||
className={cn(
|
||||
mode === "cidr" && "col-span-1",
|
||||
@@ -1030,8 +1109,9 @@ export function InternalResourceForm({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{(mode === "host" || mode === "ssh") &&
|
||||
sshServerMode !== "native" && (
|
||||
{(mode === "host" ||
|
||||
(mode === "ssh" &&
|
||||
sshServerMode !== "native")) && (
|
||||
<div className="min-w-0">
|
||||
<FormField
|
||||
control={form.control}
|
||||
@@ -1057,16 +1137,18 @@ export function InternalResourceForm({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{mode === "http" && (
|
||||
{(mode === "http" ||
|
||||
(mode === "ssh" &&
|
||||
sshServerMode !== "native")) && (
|
||||
<div className="min-w-0">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="httpHttpsPort"
|
||||
name="destinationPort"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t(
|
||||
httpHttpsPortLabelKey
|
||||
destinationPortLabelKey
|
||||
)}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
@@ -1690,6 +1772,16 @@ export function InternalResourceForm({
|
||||
"authDaemonPort",
|
||||
null
|
||||
);
|
||||
// Trim to single site
|
||||
if (selectedSites.length > 1) {
|
||||
const first =
|
||||
selectedSites.slice(0, 1);
|
||||
setSelectedSites(first);
|
||||
form.setValue(
|
||||
"siteIds",
|
||||
first.map((s) => s.siteId)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
form.setValue(
|
||||
"authDaemonMode",
|
||||
|
||||
@@ -73,7 +73,7 @@ function PrivateResourceMeta({ row }: { row: SiteResourceRow }) {
|
||||
const dest = formatSiteResourceDestinationDisplay({
|
||||
mode: row.mode,
|
||||
destination: row.destination,
|
||||
httpHttpsPort: row.destinationPort ?? null,
|
||||
destinationPort: row.destinationPort ?? null,
|
||||
scheme: row.scheme
|
||||
});
|
||||
return (
|
||||
@@ -149,7 +149,7 @@ function PrivateAccessMethod({ row }: { row: SiteResourceRow }) {
|
||||
const dest = formatSiteResourceDestinationDisplay({
|
||||
mode: row.mode,
|
||||
destination: row.destination,
|
||||
httpHttpsPort: row.destinationPort,
|
||||
destinationPort: row.destinationPort,
|
||||
scheme: row.scheme
|
||||
});
|
||||
return (
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
export type SiteResourceDestinationInput = {
|
||||
mode: "host" | "cidr" | "http";
|
||||
destination: string;
|
||||
httpHttpsPort: number | null;
|
||||
destinationPort: number | null;
|
||||
scheme: "http" | "https" | null;
|
||||
};
|
||||
|
||||
export function resolveHttpHttpsDisplayPort(
|
||||
mode: "http",
|
||||
httpHttpsPort: number | null
|
||||
destinationPort: number | null
|
||||
): number {
|
||||
if (httpHttpsPort != null) {
|
||||
return httpHttpsPort;
|
||||
if (destinationPort != null) {
|
||||
return destinationPort;
|
||||
}
|
||||
return 80;
|
||||
}
|
||||
@@ -18,11 +18,11 @@ export function resolveHttpHttpsDisplayPort(
|
||||
export function formatSiteResourceDestinationDisplay(
|
||||
row: SiteResourceDestinationInput
|
||||
): string {
|
||||
const { mode, destination, httpHttpsPort, scheme } = row;
|
||||
const { mode, destination, destinationPort, scheme } = row;
|
||||
if (mode !== "http") {
|
||||
return destination;
|
||||
}
|
||||
const port = resolveHttpHttpsDisplayPort(mode, httpHttpsPort);
|
||||
const port = resolveHttpHttpsDisplayPort(mode, destinationPort);
|
||||
const downstreamScheme = scheme ?? "http";
|
||||
const hostPart =
|
||||
destination.includes(":") && !destination.startsWith("[")
|
||||
|
||||
Reference in New Issue
Block a user