mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-23 20:50:18 +02:00
Complete removal of http and protocol from public
This commit is contained in:
@@ -106,7 +106,7 @@ export async function applyBlueprint({
|
|||||||
site.newt.newtId,
|
site.newt.newtId,
|
||||||
[target],
|
[target],
|
||||||
matchingHealthcheck ? [matchingHealthcheck] : [],
|
matchingHealthcheck ? [matchingHealthcheck] : [],
|
||||||
result.proxyResource.protocol,
|
result.proxyResource.mode === "udp" ? "udp" : "tcp",
|
||||||
site.newt.version
|
site.newt.version
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import { isValidRegionId } from "@server/db/regions";
|
|||||||
import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed";
|
import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed";
|
||||||
import { fireHealthCheckUnknownAlert } from "@server/lib/alerts";
|
import { fireHealthCheckUnknownAlert } from "@server/lib/alerts";
|
||||||
import { tierMatrix } from "../billing/tierMatrix";
|
import { tierMatrix } from "../billing/tierMatrix";
|
||||||
|
import { http } from "winston";
|
||||||
|
|
||||||
export type ProxyResourcesResults = {
|
export type ProxyResourcesResults = {
|
||||||
proxyResource: Resource;
|
proxyResource: Resource;
|
||||||
@@ -198,9 +199,6 @@ export async function updateProxyResources(
|
|||||||
)
|
)
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
const http = resourceData.protocol == "http";
|
|
||||||
const protocol =
|
|
||||||
resourceData.protocol == "http" ? "tcp" : resourceData.protocol;
|
|
||||||
const resourceEnabled =
|
const resourceEnabled =
|
||||||
resourceData.enabled == undefined || resourceData.enabled == null
|
resourceData.enabled == undefined || resourceData.enabled == null
|
||||||
? true
|
? true
|
||||||
@@ -216,7 +214,9 @@ export async function updateProxyResources(
|
|||||||
|
|
||||||
if (existingResource) {
|
if (existingResource) {
|
||||||
let domain;
|
let domain;
|
||||||
if (http) {
|
if (
|
||||||
|
["http", "ssh", "rdp", "vnc"].includes(resourceData.mode || "")
|
||||||
|
) {
|
||||||
domain = await getDomain(
|
domain = await getDomain(
|
||||||
existingResource.resourceId,
|
existingResource.resourceId,
|
||||||
resourceData["full-domain"]!,
|
resourceData["full-domain"]!,
|
||||||
@@ -246,10 +246,17 @@ export async function updateProxyResources(
|
|||||||
.update(resources)
|
.update(resources)
|
||||||
.set({
|
.set({
|
||||||
name: resourceData.name || "Unnamed Resource",
|
name: resourceData.name || "Unnamed Resource",
|
||||||
protocol: protocol || "tcp",
|
mode: resourceData.mode,
|
||||||
http: http,
|
proxyPort: ["http", "ssh", "rdp", "vnc"].includes(
|
||||||
proxyPort: http ? null : resourceData["proxy-port"],
|
resourceData.mode || ""
|
||||||
fullDomain: http ? resourceData["full-domain"] : null,
|
)
|
||||||
|
? null
|
||||||
|
: resourceData["proxy-port"],
|
||||||
|
fullDomain: ["http", "ssh", "rdp", "vnc"].includes(
|
||||||
|
resourceData.mode || ""
|
||||||
|
)
|
||||||
|
? resourceData["full-domain"]
|
||||||
|
: null,
|
||||||
subdomain: domain ? domain.subdomain : null,
|
subdomain: domain ? domain.subdomain : null,
|
||||||
domainId: domain ? domain.domainId : null,
|
domainId: domain ? domain.domainId : null,
|
||||||
wildcard: domain ? domain.wildcard : false,
|
wildcard: domain ? domain.wildcard : false,
|
||||||
@@ -466,7 +473,10 @@ export async function updateProxyResources(
|
|||||||
.set({
|
.set({
|
||||||
siteId: site.siteId,
|
siteId: site.siteId,
|
||||||
ip: targetData.hostname,
|
ip: targetData.hostname,
|
||||||
method: http ? targetData.method : null,
|
method:
|
||||||
|
resourceData.mode == "http" // the other types of ssh, rdp, and vnc use the browser gateway targets and not this one so this is okay
|
||||||
|
? targetData.method
|
||||||
|
: null,
|
||||||
port: targetData.port,
|
port: targetData.port,
|
||||||
enabled: targetData.enabled,
|
enabled: targetData.enabled,
|
||||||
path: targetData.path,
|
path: targetData.path,
|
||||||
@@ -687,7 +697,9 @@ export async function updateProxyResources(
|
|||||||
} else {
|
} else {
|
||||||
// create a brand new resource
|
// create a brand new resource
|
||||||
let domain;
|
let domain;
|
||||||
if (http) {
|
if (
|
||||||
|
["http", "ssh", "rdp", "vnc"].includes(resourceData.mode || "")
|
||||||
|
) {
|
||||||
domain = await getDomain(
|
domain = await getDomain(
|
||||||
undefined,
|
undefined,
|
||||||
resourceData["full-domain"]!,
|
resourceData["full-domain"]!,
|
||||||
@@ -711,10 +723,17 @@ export async function updateProxyResources(
|
|||||||
orgId,
|
orgId,
|
||||||
niceId: resourceNiceId,
|
niceId: resourceNiceId,
|
||||||
name: resourceData.name || "Unnamed Resource",
|
name: resourceData.name || "Unnamed Resource",
|
||||||
protocol: protocol || "tcp",
|
mode: resourceData.mode,
|
||||||
http: http,
|
proxyPort: ["http", "ssh", "rdp", "vnc"].includes(
|
||||||
proxyPort: http ? null : resourceData["proxy-port"],
|
resourceData.mode || ""
|
||||||
fullDomain: http ? resourceData["full-domain"] : null,
|
)
|
||||||
|
? null
|
||||||
|
: resourceData["proxy-port"],
|
||||||
|
fullDomain: ["http", "ssh", "rdp", "vnc"].includes(
|
||||||
|
resourceData.mode || ""
|
||||||
|
)
|
||||||
|
? resourceData["full-domain"]
|
||||||
|
: null,
|
||||||
subdomain: domain ? domain.subdomain : null,
|
subdomain: domain ? domain.subdomain : null,
|
||||||
domainId: domain ? domain.domainId : null,
|
domainId: domain ? domain.domainId : null,
|
||||||
wildcard: domain ? domain.wildcard : false,
|
wildcard: domain ? domain.wildcard : false,
|
||||||
|
|||||||
@@ -162,10 +162,13 @@ export const HeaderSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Schema for individual resource
|
// Schema for individual resource
|
||||||
export const ResourceSchema = z
|
export const PublicResourceSchema = z
|
||||||
.object({
|
.object({
|
||||||
name: z.string().optional(),
|
name: z.string().optional(),
|
||||||
protocol: z.enum(["http", "tcp", "udp"]).optional(),
|
protocol: z
|
||||||
|
.enum(["http", "tcp", "udp", "ssh", "rdp", "vnc"])
|
||||||
|
.optional(), // this was the old one and is now DEPRECATED in favor of the mode
|
||||||
|
mode: z.enum(["http", "tcp", "udp", "ssh", "rdp", "vnc"]).optional(),
|
||||||
ssl: z.boolean().optional(),
|
ssl: z.boolean().optional(),
|
||||||
scheme: z.enum(["http", "https"]).optional(),
|
scheme: z.enum(["http", "https"]).optional(),
|
||||||
"full-domain": z.string().optional(),
|
"full-domain": z.string().optional(),
|
||||||
@@ -185,9 +188,10 @@ export const ResourceSchema = z
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, require name and protocol for full resource definition
|
// Otherwise, require name and protocol/mode for full resource definition
|
||||||
return (
|
return (
|
||||||
resource.name !== undefined && resource.protocol !== undefined
|
resource.name !== undefined &&
|
||||||
|
(resource.mode !== undefined || resource.protocol !== undefined)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -201,8 +205,8 @@ export const ResourceSchema = z
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If protocol is http, all targets must have method field
|
// If protocol/mode is http, all targets must have method field
|
||||||
if (resource.protocol === "http") {
|
if ((resource.mode ?? resource.protocol) === "http") {
|
||||||
return resource.targets.every(
|
return resource.targets.every(
|
||||||
(target) => target == null || target.method !== undefined
|
(target) => target == null || target.method !== undefined
|
||||||
);
|
);
|
||||||
@@ -220,8 +224,9 @@ export const ResourceSchema = z
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If protocol is tcp or udp, no target should have method field
|
// If protocol/mode is tcp or udp, no target should have method field
|
||||||
if (resource.protocol === "tcp" || resource.protocol === "udp") {
|
const effectiveProtocol1 = resource.mode ?? resource.protocol;
|
||||||
|
if (effectiveProtocol1 === "tcp" || effectiveProtocol1 === "udp") {
|
||||||
return resource.targets.every(
|
return resource.targets.every(
|
||||||
(target) => target == null || target.method === undefined
|
(target) => target == null || target.method === undefined
|
||||||
);
|
);
|
||||||
@@ -239,8 +244,8 @@ export const ResourceSchema = z
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If protocol is http, it must have a full-domain
|
// If protocol/mode is http, it must have a full-domain
|
||||||
if (resource.protocol === "http") {
|
if ((resource.mode ?? resource.protocol) === "http") {
|
||||||
return (
|
return (
|
||||||
resource["full-domain"] !== undefined &&
|
resource["full-domain"] !== undefined &&
|
||||||
resource["full-domain"].length > 0
|
resource["full-domain"].length > 0
|
||||||
@@ -259,8 +264,9 @@ export const ResourceSchema = z
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If protocol is tcp or udp, it must have both proxy-port
|
// If protocol/mode is tcp or udp, it must have both proxy-port
|
||||||
if (resource.protocol === "tcp" || resource.protocol === "udp") {
|
const effectiveProtocol2 = resource.mode ?? resource.protocol;
|
||||||
|
if (effectiveProtocol2 === "tcp" || effectiveProtocol2 === "udp") {
|
||||||
return resource["proxy-port"] !== undefined;
|
return resource["proxy-port"] !== undefined;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -277,8 +283,9 @@ export const ResourceSchema = z
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If protocol is tcp or udp, it must not have auth
|
// If protocol/mode is tcp or udp, it must not have auth
|
||||||
if (resource.protocol === "tcp" || resource.protocol === "udp") {
|
const effectiveProtocol3 = resource.mode ?? resource.protocol;
|
||||||
|
if (effectiveProtocol3 === "tcp" || effectiveProtocol3 === "udp") {
|
||||||
return resource.auth === undefined;
|
return resource.auth === undefined;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -340,7 +347,8 @@ export const ResourceSchema = z
|
|||||||
if (parts.includes("*", 1)) return false; // no further wildcards
|
if (parts.includes("*", 1)) return false; // no further wildcards
|
||||||
if (parts.length < 3) return false; // need at least *.label.tld
|
if (parts.length < 3) return false; // need at least *.label.tld
|
||||||
|
|
||||||
const labelRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$|^[a-zA-Z0-9]$/;
|
const labelRegex =
|
||||||
|
/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$|^[a-zA-Z0-9]$/;
|
||||||
return parts.slice(1).every((label) => labelRegex.test(label));
|
return parts.slice(1).every((label) => labelRegex.test(label));
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -348,16 +356,23 @@ export const ResourceSchema = z
|
|||||||
message:
|
message:
|
||||||
'Wildcard full-domain must have "*" as the leftmost label only, followed by at least two valid hostname labels (e.g. "*.example.com" or "*.level1.example.com"). Patterns like "*example.com" or "level2.*.example.com" are not supported.'
|
'Wildcard full-domain must have "*" as the leftmost label only, followed by at least two valid hostname labels (e.g. "*.example.com" or "*.level1.example.com"). Patterns like "*example.com" or "level2.*.example.com" are not supported.'
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
.transform((resource) => {
|
||||||
|
// Normalize: prefer mode, fall back to protocol for backwards compatibility
|
||||||
|
if (resource.mode === undefined && resource.protocol !== undefined) {
|
||||||
|
resource.mode = resource.protocol;
|
||||||
|
}
|
||||||
|
return resource;
|
||||||
|
});
|
||||||
|
|
||||||
export function isTargetsOnlyResource(resource: any): boolean {
|
export function isTargetsOnlyResource(resource: any): boolean {
|
||||||
return Object.keys(resource).length === 1 && resource.targets;
|
return Object.keys(resource).length === 1 && resource.targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ClientResourceSchema = z
|
export const PrivateResourceSchema = z
|
||||||
.object({
|
.object({
|
||||||
name: z.string().min(1).max(255),
|
name: z.string().min(1).max(255),
|
||||||
mode: z.enum(["host", "cidr", "http"]),
|
mode: z.enum(["host", "cidr", "http", "ssh"]),
|
||||||
site: z.string().optional(), // DEPRECATED IN FAVOR OF sites
|
site: z.string().optional(), // DEPRECATED IN FAVOR OF sites
|
||||||
sites: z.array(z.string()).optional().default([]),
|
sites: z.array(z.string()).optional().default([]),
|
||||||
// protocol: z.enum(["tcp", "udp"]).optional(),
|
// protocol: z.enum(["tcp", "udp"]).optional(),
|
||||||
@@ -435,19 +450,19 @@ export const ClientResourceSchema = z
|
|||||||
export const ConfigSchema = z
|
export const ConfigSchema = z
|
||||||
.object({
|
.object({
|
||||||
"proxy-resources": z
|
"proxy-resources": z
|
||||||
.record(z.string(), ResourceSchema)
|
.record(z.string(), PublicResourceSchema)
|
||||||
.optional()
|
.optional()
|
||||||
.prefault({}),
|
.prefault({}),
|
||||||
"public-resources": z
|
"public-resources": z
|
||||||
.record(z.string(), ResourceSchema)
|
.record(z.string(), PublicResourceSchema)
|
||||||
.optional()
|
.optional()
|
||||||
.prefault({}),
|
.prefault({}),
|
||||||
"client-resources": z
|
"client-resources": z
|
||||||
.record(z.string(), ClientResourceSchema)
|
.record(z.string(), PrivateResourceSchema)
|
||||||
.optional()
|
.optional()
|
||||||
.prefault({}),
|
.prefault({}),
|
||||||
"private-resources": z
|
"private-resources": z
|
||||||
.record(z.string(), ClientResourceSchema)
|
.record(z.string(), PrivateResourceSchema)
|
||||||
.optional()
|
.optional()
|
||||||
.prefault({}),
|
.prefault({}),
|
||||||
sites: z.record(z.string(), SiteSchema).optional().prefault({})
|
sites: z.record(z.string(), SiteSchema).optional().prefault({})
|
||||||
@@ -472,10 +487,13 @@ export const ConfigSchema = z
|
|||||||
}
|
}
|
||||||
|
|
||||||
return data as {
|
return data as {
|
||||||
"proxy-resources": Record<string, z.infer<typeof ResourceSchema>>;
|
"proxy-resources": Record<
|
||||||
|
string,
|
||||||
|
z.infer<typeof PublicResourceSchema>
|
||||||
|
>;
|
||||||
"client-resources": Record<
|
"client-resources": Record<
|
||||||
string,
|
string,
|
||||||
z.infer<typeof ClientResourceSchema>
|
z.infer<typeof PrivateResourceSchema>
|
||||||
>;
|
>;
|
||||||
sites: Record<string, z.infer<typeof SiteSchema>>;
|
sites: Record<string, z.infer<typeof SiteSchema>>;
|
||||||
};
|
};
|
||||||
@@ -614,5 +632,5 @@ export const ConfigSchema = z
|
|||||||
// Type inference from the schema
|
// Type inference from the schema
|
||||||
export type Site = z.infer<typeof SiteSchema>;
|
export type Site = z.infer<typeof SiteSchema>;
|
||||||
export type Target = z.infer<typeof TargetSchema>;
|
export type Target = z.infer<typeof TargetSchema>;
|
||||||
export type Resource = z.infer<typeof ResourceSchema>;
|
export type Resource = z.infer<typeof PublicResourceSchema>;
|
||||||
export type Config = z.infer<typeof ConfigSchema>;
|
export type Config = z.infer<typeof ConfigSchema>;
|
||||||
|
|||||||
+10
-4
@@ -2,7 +2,14 @@ import { PostHog } from "posthog-node";
|
|||||||
import config from "./config";
|
import config from "./config";
|
||||||
import { getHostMeta } from "./hostMeta";
|
import { getHostMeta } from "./hostMeta";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { alertRules, apiKeys, blueprints, db, roles, siteResources } from "@server/db";
|
import {
|
||||||
|
alertRules,
|
||||||
|
apiKeys,
|
||||||
|
blueprints,
|
||||||
|
db,
|
||||||
|
roles,
|
||||||
|
siteResources
|
||||||
|
} from "@server/db";
|
||||||
import { sites, users, orgs, resources, clients, idp } from "@server/db";
|
import { sites, users, orgs, resources, clients, idp } from "@server/db";
|
||||||
import { eq, count, notInArray, and, isNotNull, isNull } from "drizzle-orm";
|
import { eq, count, notInArray, and, isNotNull, isNull } from "drizzle-orm";
|
||||||
import { APP_VERSION } from "./consts";
|
import { APP_VERSION } from "./consts";
|
||||||
@@ -143,8 +150,7 @@ class TelemetryClient {
|
|||||||
.select({
|
.select({
|
||||||
name: resources.name,
|
name: resources.name,
|
||||||
sso: resources.sso,
|
sso: resources.sso,
|
||||||
protocol: resources.protocol,
|
mode: resources.mode
|
||||||
http: resources.http
|
|
||||||
})
|
})
|
||||||
.from(resources);
|
.from(resources);
|
||||||
|
|
||||||
@@ -311,7 +317,7 @@ class TelemetryClient {
|
|||||||
(r) => r.sso
|
(r) => r.sso
|
||||||
).length,
|
).length,
|
||||||
num_resources_non_http: stats.resources.filter(
|
num_resources_non_http: stats.resources.filter(
|
||||||
(r) => !r.http
|
(r) => r.mode !== "http"
|
||||||
).length,
|
).length,
|
||||||
num_newt_sites: stats.sites.filter((s) => s.type === "newt")
|
num_newt_sites: stats.sites.filter((s) => s.type === "newt")
|
||||||
.length,
|
.length,
|
||||||
|
|||||||
@@ -55,9 +55,7 @@ export async function getTraefikConfig(
|
|||||||
resourceName: resources.name,
|
resourceName: resources.name,
|
||||||
fullDomain: resources.fullDomain,
|
fullDomain: resources.fullDomain,
|
||||||
ssl: resources.ssl,
|
ssl: resources.ssl,
|
||||||
http: resources.http,
|
|
||||||
proxyPort: resources.proxyPort,
|
proxyPort: resources.proxyPort,
|
||||||
protocol: resources.protocol,
|
|
||||||
subdomain: resources.subdomain,
|
subdomain: resources.subdomain,
|
||||||
domainId: resources.domainId,
|
domainId: resources.domainId,
|
||||||
enabled: resources.enabled,
|
enabled: resources.enabled,
|
||||||
@@ -68,6 +66,7 @@ export async function getTraefikConfig(
|
|||||||
headers: resources.headers,
|
headers: resources.headers,
|
||||||
proxyProtocol: resources.proxyProtocol,
|
proxyProtocol: resources.proxyProtocol,
|
||||||
proxyProtocolVersion: resources.proxyProtocolVersion,
|
proxyProtocolVersion: resources.proxyProtocolVersion,
|
||||||
|
mode: resources.mode,
|
||||||
|
|
||||||
// Target fields
|
// Target fields
|
||||||
targetId: targets.targetId,
|
targetId: targets.targetId,
|
||||||
@@ -115,8 +114,8 @@ export async function getTraefikConfig(
|
|||||||
),
|
),
|
||||||
inArray(sites.type, siteTypes),
|
inArray(sites.type, siteTypes),
|
||||||
allowRawResources
|
allowRawResources
|
||||||
? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true
|
? inArray(resources.mode, ["http", "udp", "tcp"]) // allow all three
|
||||||
: eq(resources.http, true)
|
: eq(resources.mode, "http")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.orderBy(desc(targets.priority), targets.targetId); // stable ordering
|
.orderBy(desc(targets.priority), targets.targetId); // stable ordering
|
||||||
@@ -166,9 +165,8 @@ export async function getTraefikConfig(
|
|||||||
key: key,
|
key: key,
|
||||||
fullDomain: row.fullDomain,
|
fullDomain: row.fullDomain,
|
||||||
ssl: row.ssl,
|
ssl: row.ssl,
|
||||||
http: row.http,
|
mode: row.mode,
|
||||||
proxyPort: row.proxyPort,
|
proxyPort: row.proxyPort,
|
||||||
protocol: row.protocol,
|
|
||||||
subdomain: row.subdomain,
|
subdomain: row.subdomain,
|
||||||
domainId: row.domainId,
|
domainId: row.domainId,
|
||||||
enabled: row.enabled,
|
enabled: row.enabled,
|
||||||
@@ -580,7 +578,7 @@ export async function getTraefikConfig(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const protocol = resource.protocol.toLowerCase();
|
const protocol = resource.mode === "udp" ? "udp" : "tcp"; // all of the other ones are tcp
|
||||||
const port = resource.proxyPort;
|
const port = resource.proxyPort;
|
||||||
|
|
||||||
if (!port) {
|
if (!port) {
|
||||||
|
|||||||
@@ -944,7 +944,7 @@ export async function getTraefikConfig(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const protocol = resource.protocol.toLowerCase();
|
const protocol = resource.mode == "udp" ? "udp" : "tcp";
|
||||||
const port = resource.proxyPort;
|
const port = resource.proxyPort;
|
||||||
|
|
||||||
if (!port) {
|
if (!port) {
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ export async function buildTargetConfigurationForNewtClient(
|
|||||||
siteId: number,
|
siteId: number,
|
||||||
version?: string | null
|
version?: string | null
|
||||||
) {
|
) {
|
||||||
// Get all enabled targets with their resource protocol information
|
// Get all enabled targets with their resource mode information
|
||||||
const allTargets = await db
|
const allTargets = await db
|
||||||
.select({
|
.select({
|
||||||
resourceId: targets.resourceId,
|
resourceId: targets.resourceId,
|
||||||
@@ -207,7 +207,7 @@ export async function buildTargetConfigurationForNewtClient(
|
|||||||
port: targets.port,
|
port: targets.port,
|
||||||
internalPort: targets.internalPort,
|
internalPort: targets.internalPort,
|
||||||
enabled: targets.enabled,
|
enabled: targets.enabled,
|
||||||
protocol: resources.protocol
|
mode: resources.mode
|
||||||
})
|
})
|
||||||
.from(targets)
|
.from(targets)
|
||||||
.innerJoin(resources, eq(targets.resourceId, resources.resourceId))
|
.innerJoin(resources, eq(targets.resourceId, resources.resourceId))
|
||||||
@@ -252,10 +252,11 @@ export async function buildTargetConfigurationForNewtClient(
|
|||||||
const formattedTarget = `${target.internalPort}:${formatEndpoint(target.ip, target.port)}`;
|
const formattedTarget = `${target.internalPort}:${formatEndpoint(target.ip, target.port)}`;
|
||||||
|
|
||||||
// Add to the appropriate protocol array
|
// Add to the appropriate protocol array
|
||||||
if (target.protocol === "tcp") {
|
if (target.mode === "udp") {
|
||||||
acc.tcpTargets.push(formattedTarget);
|
|
||||||
} else {
|
|
||||||
acc.udpTargets.push(formattedTarget);
|
acc.udpTargets.push(formattedTarget);
|
||||||
|
} else {
|
||||||
|
// all other modes are tcp
|
||||||
|
acc.tcpTargets.push(formattedTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export async function createResourceRule(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resource.http) {
|
if (!["http", "ssh", "rdp", "vnc"].includes(resource.mode)) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export async function deleteResource(
|
|||||||
// [target],
|
// [target],
|
||||||
[], // deleting the target from newt causes issues because we cant unbind the port. this needs to be fixed in newt before we can do this
|
[], // deleting the target from newt causes issues because we cant unbind the port. this needs to be fixed in newt before we can do this
|
||||||
healthChecksToBeRemoved,
|
healthChecksToBeRemoved,
|
||||||
deletedResource.protocol,
|
deletedResource.mode === "udp" ? "udp" : "tcp",
|
||||||
newt.version
|
newt.version
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ export async function getUserResources(
|
|||||||
ssl: boolean;
|
ssl: boolean;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
sso: boolean;
|
sso: boolean;
|
||||||
protocol: string;
|
mode: string;
|
||||||
emailWhitelistEnabled: boolean;
|
emailWhitelistEnabled: boolean;
|
||||||
}> = [];
|
}> = [];
|
||||||
if (accessibleResourceIds.length > 0) {
|
if (accessibleResourceIds.length > 0) {
|
||||||
@@ -132,7 +132,7 @@ export async function getUserResources(
|
|||||||
ssl: resources.ssl,
|
ssl: resources.ssl,
|
||||||
enabled: resources.enabled,
|
enabled: resources.enabled,
|
||||||
sso: resources.sso,
|
sso: resources.sso,
|
||||||
protocol: resources.protocol,
|
mode: resources.mode,
|
||||||
emailWhitelistEnabled: resources.emailWhitelistEnabled
|
emailWhitelistEnabled: resources.emailWhitelistEnabled
|
||||||
})
|
})
|
||||||
.from(resources)
|
.from(resources)
|
||||||
@@ -316,7 +316,7 @@ export async function getUserResources(
|
|||||||
hasPincode ||
|
hasPincode ||
|
||||||
hasWhitelist
|
hasWhitelist
|
||||||
),
|
),
|
||||||
protocol: resource.protocol,
|
mode: resource.mode,
|
||||||
sso: resource.sso,
|
sso: resource.sso,
|
||||||
password: hasPassword,
|
password: hasPassword,
|
||||||
pincode: hasPincode,
|
pincode: hasPincode,
|
||||||
@@ -332,7 +332,6 @@ export async function getUserResources(
|
|||||||
name: siteResource.name,
|
name: siteResource.name,
|
||||||
destination: siteResource.destination,
|
destination: siteResource.destination,
|
||||||
mode: siteResource.mode,
|
mode: siteResource.mode,
|
||||||
protocol: siteResource.scheme,
|
|
||||||
ssl: siteResource.ssl,
|
ssl: siteResource.ssl,
|
||||||
fullDomain: siteResource.fullDomain,
|
fullDomain: siteResource.fullDomain,
|
||||||
enabled: siteResource.enabled,
|
enabled: siteResource.enabled,
|
||||||
@@ -380,14 +379,13 @@ export type GetUserResourcesResponse = {
|
|||||||
domain: string;
|
domain: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
protected: boolean;
|
protected: boolean;
|
||||||
protocol: string;
|
mode: string;
|
||||||
}>;
|
}>;
|
||||||
siteResources: Array<{
|
siteResources: Array<{
|
||||||
siteResourceId: number;
|
siteResourceId: number;
|
||||||
name: string;
|
name: string;
|
||||||
destination: string;
|
destination: string;
|
||||||
mode: string;
|
mode: string;
|
||||||
protocol: string | null;
|
|
||||||
tcpPortRangeString: string | null;
|
tcpPortRangeString: string | null;
|
||||||
udpPortRangeString: string | null;
|
udpPortRangeString: string | null;
|
||||||
disableIcmp: boolean | null;
|
disableIcmp: boolean | null;
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ export async function updateResource(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource.http) {
|
if (["http", "ssh", "rdp", "vnc"].includes(resource.mode)) {
|
||||||
// HANDLE UPDATING HTTP RESOURCES
|
// HANDLE UPDATING HTTP RESOURCES
|
||||||
return await updateHttpResource(
|
return await updateHttpResource(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ const updateResourceRuleParamsSchema = z.strictObject({
|
|||||||
const updateResourceRuleSchema = z
|
const updateResourceRuleSchema = z
|
||||||
.strictObject({
|
.strictObject({
|
||||||
action: z.enum(["ACCEPT", "DROP", "PASS"]).optional(),
|
action: z.enum(["ACCEPT", "DROP", "PASS"]).optional(),
|
||||||
match: z.enum(["CIDR", "IP", "PATH", "COUNTRY", "ASN", "REGION"]).optional(),
|
match: z
|
||||||
|
.enum(["CIDR", "IP", "PATH", "COUNTRY", "ASN", "REGION"])
|
||||||
|
.optional(),
|
||||||
value: z.string().min(1).optional(),
|
value: z.string().min(1).optional(),
|
||||||
priority: z.int(),
|
priority: z.int(),
|
||||||
enabled: z.boolean().optional()
|
enabled: z.boolean().optional()
|
||||||
@@ -102,7 +104,7 @@ export async function updateResourceRule(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resource.http) {
|
if (!["http", "ssh", "rdp", "vnc"].includes(resource.mode)) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
|
|||||||
@@ -314,7 +314,7 @@ export async function createTarget(
|
|||||||
newt.newtId,
|
newt.newtId,
|
||||||
newTarget,
|
newTarget,
|
||||||
healthCheck,
|
healthCheck,
|
||||||
resource.protocol,
|
resource.mode === "udp" ? "udp" : "tcp",
|
||||||
newt.version
|
newt.version
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ export async function deleteTarget(
|
|||||||
// [deletedTarget],
|
// [deletedTarget],
|
||||||
[], // deleting the target from newt causes issues because we cant unbind the port. this needs to be fixed in newt before we can do this
|
[], // deleting the target from newt causes issues because we cant unbind the port. this needs to be fixed in newt before we can do this
|
||||||
[deletedHealthCheck],
|
[deletedHealthCheck],
|
||||||
resource.protocol,
|
resource.mode === "udp" ? "udp" : "tcp",
|
||||||
newt.version
|
newt.version
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ export async function updateTarget(
|
|||||||
newt.newtId,
|
newt.newtId,
|
||||||
[updatedTarget],
|
[updatedTarget],
|
||||||
[updatedHc],
|
[updatedHc],
|
||||||
resource.protocol,
|
resource.mode === "udp" ? "udp" : "tcp",
|
||||||
newt.version
|
newt.version
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ function MaintenanceSectionForm({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resource.http) {
|
if (!["http", "ssh", "rdp", "vnc"].includes(resource.mode)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +176,9 @@ function MaintenanceSectionForm({
|
|||||||
render={({ field }) => {
|
render={({ field }) => {
|
||||||
const isDisabled =
|
const isDisabled =
|
||||||
!isPaidUser(tierMatrix.maintencePage) ||
|
!isPaidUser(tierMatrix.maintencePage) ||
|
||||||
resource.http === false;
|
!["http", "ssh", "rdp", "vnc"].includes(
|
||||||
|
resource.mode
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
@@ -462,14 +464,14 @@ export default function GeneralForm() {
|
|||||||
.refine(
|
.refine(
|
||||||
(data) => {
|
(data) => {
|
||||||
// For non-HTTP resources, proxyPort should be defined
|
// For non-HTTP resources, proxyPort should be defined
|
||||||
if (!resource.http) {
|
if (!["http", "ssh", "rdp", "vnc"].includes(resource.mode)) {
|
||||||
return data.proxyPort !== undefined;
|
return data.proxyPort !== undefined;
|
||||||
}
|
}
|
||||||
// For HTTP resources, proxyPort should be undefined
|
// For HTTP resources, proxyPort should be undefined
|
||||||
return data.proxyPort === undefined;
|
return data.proxyPort === undefined;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message: !resource.http
|
message: !["http", "ssh", "rdp", "vnc"].includes(resource.mode)
|
||||||
? "Port number is required for non-HTTP resources"
|
? "Port number is required for non-HTTP resources"
|
||||||
: "Port number should not be set for HTTP resources",
|
: "Port number should not be set for HTTP resources",
|
||||||
path: ["proxyPort"]
|
path: ["proxyPort"]
|
||||||
@@ -623,7 +625,9 @@ export default function GeneralForm() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!resource.http && (
|
{!["http", "ssh", "rdp", "vnc"].includes(
|
||||||
|
resource.mode
|
||||||
|
) && (
|
||||||
<>
|
<>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
@@ -672,7 +676,9 @@ export default function GeneralForm() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{resource.http && (
|
{["http", "ssh", "rdp", "vnc"].includes(
|
||||||
|
resource.mode
|
||||||
|
) && (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div id="resource-domain-picker">
|
<div id="resource-domain-picker">
|
||||||
<DomainPicker
|
<DomainPicker
|
||||||
|
|||||||
@@ -130,20 +130,20 @@ export default function ReverseProxyTargetsPage(props: {
|
|||||||
<SettingsContainer>
|
<SettingsContainer>
|
||||||
<ProxyResourceTargetsForm
|
<ProxyResourceTargetsForm
|
||||||
orgId={params.orgId}
|
orgId={params.orgId}
|
||||||
isHttp={resource.http}
|
isHttp={["http", "ssh", "rdp", "vnc"].includes(resource.mode)}
|
||||||
initialTargets={remoteTargets}
|
initialTargets={remoteTargets}
|
||||||
resource={resource}
|
resource={resource}
|
||||||
updateResource={updateResource}
|
updateResource={updateResource}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{resource.http && (
|
{["http", "ssh", "rdp", "vnc"].includes(resource.mode) && (
|
||||||
<ProxyResourceHttpForm
|
<ProxyResourceHttpForm
|
||||||
resource={resource}
|
resource={resource}
|
||||||
updateResource={updateResource}
|
updateResource={updateResource}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!resource.http && resource.protocol == "tcp" && (
|
{resource.mode == "tcp" && (
|
||||||
<ProxyResourceProtocolForm
|
<ProxyResourceProtocolForm
|
||||||
resource={resource}
|
resource={resource}
|
||||||
updateResource={updateResource}
|
updateResource={updateResource}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export default async function ResourceLayout(props: ResourceLayoutProps) {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
if (resource.http) {
|
if (["http", "ssh", "rdp", "vnc"].includes(resource.mode)) {
|
||||||
navItems.push({
|
navItems.push({
|
||||||
title: t("authentication"),
|
title: t("authentication"),
|
||||||
href: `/{orgId}/settings/resources/proxy/{niceId}/authentication`
|
href: `/{orgId}/settings/resources/proxy/{niceId}/authentication`
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export default function ResourceRules(props: {
|
|||||||
resolver: zodResolver(addRuleSchema),
|
resolver: zodResolver(addRuleSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
action: "ACCEPT",
|
action: "ACCEPT",
|
||||||
match: resource.http && resource.mode == "http" ? "PATH" : "IP",
|
match: resource.mode == "http" ? "PATH" : "IP",
|
||||||
value: ""
|
value: ""
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -577,7 +577,7 @@ export default function ResourceRules(props: {
|
|||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{resource.http && resource.mode == "http" && (
|
{resource.mode == "http" && (
|
||||||
<SelectItem value="PATH">
|
<SelectItem value="PATH">
|
||||||
{RuleMatch.PATH}
|
{RuleMatch.PATH}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
@@ -1037,15 +1037,14 @@ export default function ResourceRules(props: {
|
|||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{resource.http &&
|
{resource.mode ==
|
||||||
resource.mode ==
|
"http" && (
|
||||||
"http" && (
|
<SelectItem value="PATH">
|
||||||
<SelectItem value="PATH">
|
{
|
||||||
{
|
RuleMatch.PATH
|
||||||
RuleMatch.PATH
|
}
|
||||||
}
|
</SelectItem>
|
||||||
</SelectItem>
|
)}
|
||||||
)}
|
|
||||||
<SelectItem value="IP">
|
<SelectItem value="IP">
|
||||||
{RuleMatch.IP}
|
{RuleMatch.IP}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
|
|||||||
@@ -108,11 +108,11 @@ export default async function ProxyResourcesPage(
|
|||||||
orgId: params.orgId,
|
orgId: params.orgId,
|
||||||
nice: resource.niceId,
|
nice: resource.niceId,
|
||||||
domain: `${resource.ssl ? "https://" : "http://"}${toUnicode(resource.fullDomain || "")}`,
|
domain: `${resource.ssl ? "https://" : "http://"}${toUnicode(resource.fullDomain || "")}`,
|
||||||
protocol: resource.protocol,
|
|
||||||
proxyPort: resource.proxyPort,
|
proxyPort: resource.proxyPort,
|
||||||
http: resource.http,
|
|
||||||
labels: resource.labels,
|
labels: resource.labels,
|
||||||
authState: !resource.http
|
authState: !["http", "ssh", "rdp", "vnc"].includes(
|
||||||
|
resource.mode || ""
|
||||||
|
)
|
||||||
? "none"
|
? "none"
|
||||||
: resource.sso ||
|
: resource.sso ||
|
||||||
resource.pincodeId !== null ||
|
resource.pincodeId !== null ||
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ type Resource = {
|
|||||||
domain: string;
|
domain: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
protected: boolean;
|
protected: boolean;
|
||||||
protocol: string;
|
mode: string; // "http", "tcp", "udp", "rdp", "vnc", "ssh"
|
||||||
// Auth method fields
|
// Auth method fields
|
||||||
sso?: boolean;
|
sso?: boolean;
|
||||||
password?: boolean;
|
password?: boolean;
|
||||||
@@ -64,7 +64,6 @@ type SiteResource = {
|
|||||||
name: string;
|
name: string;
|
||||||
destination: string;
|
destination: string;
|
||||||
mode: string;
|
mode: string;
|
||||||
protocol: string | null;
|
|
||||||
ssl: boolean;
|
ssl: boolean;
|
||||||
fullDomain: string | null;
|
fullDomain: string | null;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
@@ -882,21 +881,6 @@ export default function MemberResourcesPortal({
|
|||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{siteResource.protocol && (
|
|
||||||
<div>
|
|
||||||
<span className="font-medium">
|
|
||||||
{t(
|
|
||||||
"protocol"
|
|
||||||
)}
|
|
||||||
:
|
|
||||||
</span>
|
|
||||||
<span className="ml-2 text-muted-foreground uppercase">
|
|
||||||
{
|
|
||||||
siteResource.protocol
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{t(
|
{t(
|
||||||
@@ -954,7 +938,7 @@ export default function MemberResourcesPortal({
|
|||||||
siteResource.fullDomain ? (
|
siteResource.fullDomain ? (
|
||||||
/* HTTP mode - show as clickable link */
|
/* HTTP mode - show as clickable link */
|
||||||
<CopyToClipboard
|
<CopyToClipboard
|
||||||
text={`${siteResource.ssl ? "https" : (siteResource.protocol ?? "http")}://${siteResource.fullDomain}`}
|
text={`${siteResource.ssl ? "https" : (siteResource.mode ?? "http")}://${siteResource.fullDomain}`}
|
||||||
isLink={true}
|
isLink={true}
|
||||||
/>
|
/>
|
||||||
) : siteResource.alias ? (
|
) : siteResource.alias ? (
|
||||||
@@ -1037,7 +1021,7 @@ export default function MemberResourcesPortal({
|
|||||||
<Button
|
<Button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
window.open(
|
window.open(
|
||||||
`${siteResource.ssl ? "https" : (siteResource.protocol ?? "http")}://${siteResource.fullDomain}`,
|
`${siteResource.ssl ? "https" : (siteResource.mode ?? "http")}://${siteResource.fullDomain}`,
|
||||||
"_blank"
|
"_blank"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,8 +90,6 @@ export type ResourceRow = {
|
|||||||
domain: string;
|
domain: string;
|
||||||
mode: string | null;
|
mode: string | null;
|
||||||
authState: string;
|
authState: string;
|
||||||
http: boolean;
|
|
||||||
protocol: string;
|
|
||||||
proxyPort: number | null;
|
proxyPort: number | null;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
domainId?: string;
|
domainId?: string;
|
||||||
@@ -365,11 +363,11 @@ export default function ProxyResourcesTable({
|
|||||||
const resourceRow = row.original;
|
const resourceRow = row.original;
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{resourceRow.http
|
{resourceRow.mode == "http"
|
||||||
? resourceRow.ssl
|
? resourceRow.ssl
|
||||||
? "HTTPS"
|
? "HTTPS"
|
||||||
: "HTTP"
|
: "HTTP"
|
||||||
: resourceRow.protocol.toUpperCase()}
|
: resourceRow.mode?.toUpperCase()}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -412,7 +410,7 @@ export default function ProxyResourcesTable({
|
|||||||
),
|
),
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const resourceRow = row.original;
|
const resourceRow = row.original;
|
||||||
if (!resourceRow.http || resourceRow.mode !== "http") {
|
if (resourceRow.mode !== "http") {
|
||||||
return <span>-</span>;
|
return <span>-</span>;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@@ -443,7 +441,7 @@ export default function ProxyResourcesTable({
|
|||||||
header: () => <span className="p-3">{t("uptime30d")}</span>,
|
header: () => <span className="p-3">{t("uptime30d")}</span>,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const resourceRow = row.original;
|
const resourceRow = row.original;
|
||||||
if (!resourceRow.http || resourceRow.mode !== "http") {
|
if (resourceRow.mode !== "http") {
|
||||||
return <span>-</span>;
|
return <span>-</span>;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@@ -458,7 +456,11 @@ export default function ProxyResourcesTable({
|
|||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const resourceRow = row.original;
|
const resourceRow = row.original;
|
||||||
|
|
||||||
if (!resourceRow.http) {
|
if (
|
||||||
|
!["http", "ssh", "rdp", "vnc"].includes(
|
||||||
|
resourceRow.mode || ""
|
||||||
|
)
|
||||||
|
) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2 min-w-0">
|
<div className="flex items-center gap-2 min-w-0">
|
||||||
<CopyToClipboard
|
<CopyToClipboard
|
||||||
@@ -975,7 +977,7 @@ function ResourceEnabledForm({
|
|||||||
resource,
|
resource,
|
||||||
onToggleResourceEnabled
|
onToggleResourceEnabled
|
||||||
}: ResourceEnabledFormProps) {
|
}: ResourceEnabledFormProps) {
|
||||||
const enabled = resource.http
|
const enabled = ["http", "ssh", "rdp", "vnc"].includes(resource.mode || "")
|
||||||
? !!resource.domainId && resource.enabled
|
? !!resource.domainId && resource.enabled
|
||||||
: resource.enabled;
|
: resource.enabled;
|
||||||
const [optimisticEnabled, setOptimisticEnabled] = useOptimistic(enabled);
|
const [optimisticEnabled, setOptimisticEnabled] = useOptimistic(enabled);
|
||||||
@@ -993,7 +995,10 @@ function ResourceEnabledForm({
|
|||||||
<Switch
|
<Switch
|
||||||
checked={optimisticEnabled}
|
checked={optimisticEnabled}
|
||||||
disabled={
|
disabled={
|
||||||
(resource.http && !resource.domainId) ||
|
(["http", "ssh", "rdp", "vnc"].includes(
|
||||||
|
resource.mode || ""
|
||||||
|
) &&
|
||||||
|
!resource.domainId) ||
|
||||||
optimisticEnabled !== enabled
|
optimisticEnabled !== enabled
|
||||||
}
|
}
|
||||||
name="enabled"
|
name="enabled"
|
||||||
|
|||||||
@@ -31,12 +31,14 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) {
|
|||||||
const fullUrl = `${resource.ssl ? "https" : "http"}://${toUnicode(resource.fullDomain || "")}`;
|
const fullUrl = `${resource.ssl ? "https" : "http"}://${toUnicode(resource.fullDomain || "")}`;
|
||||||
|
|
||||||
const showCertificate = !!(
|
const showCertificate = !!(
|
||||||
resource.http &&
|
["http", "ssh", "rdp", "vnc"].includes(resource.mode) &&
|
||||||
resource.domainId &&
|
resource.domainId &&
|
||||||
resource.fullDomain &&
|
resource.fullDomain &&
|
||||||
build != "oss"
|
build != "oss"
|
||||||
);
|
);
|
||||||
const showType = !!(resource.http && resource.mode);
|
const showType = !!(
|
||||||
|
["http", "ssh", "rdp", "vnc"].includes(resource.mode) && resource.mode
|
||||||
|
);
|
||||||
const showHealth =
|
const showHealth =
|
||||||
!["ssh", "rdp", "vnc"].includes(resource.mode || "") &&
|
!["ssh", "rdp", "vnc"].includes(resource.mode || "") &&
|
||||||
!!resource.health &&
|
!!resource.health &&
|
||||||
@@ -64,7 +66,7 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) {
|
|||||||
</span>
|
</span>
|
||||||
</InfoSectionContent>
|
</InfoSectionContent>
|
||||||
</InfoSection> */}
|
</InfoSection> */}
|
||||||
{resource.http ? (
|
{["http", "ssh", "rdp", "vnc"].includes(resource.mode) ? (
|
||||||
<>
|
<>
|
||||||
<InfoSection>
|
<InfoSection>
|
||||||
<InfoSectionTitle>URL</InfoSectionTitle>
|
<InfoSectionTitle>URL</InfoSectionTitle>
|
||||||
@@ -124,7 +126,7 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) {
|
|||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
<span className="inline-flex items-center">
|
<span className="inline-flex items-center">
|
||||||
{resource.protocol.toUpperCase()}
|
{resource.mode?.toUpperCase()}
|
||||||
</span>
|
</span>
|
||||||
</InfoSectionContent>
|
</InfoSectionContent>
|
||||||
</InfoSection>
|
</InfoSection>
|
||||||
|
|||||||
@@ -44,13 +44,13 @@ function isSafeUrlForLink(href: string): boolean {
|
|||||||
const OVERVIEW_META_CLASS = "w-full min-w-0 text-muted-foreground text-sm";
|
const OVERVIEW_META_CLASS = "w-full min-w-0 text-muted-foreground text-sm";
|
||||||
|
|
||||||
function publicProtocolLabel(r: PublicResourceRow): string {
|
function publicProtocolLabel(r: PublicResourceRow): string {
|
||||||
if (r.http) {
|
if (r.mode == "http") {
|
||||||
return r.ssl ? "HTTPS" : "HTTP";
|
return r.ssl ? "HTTPS" : "HTTP";
|
||||||
}
|
}
|
||||||
const p = (r.protocol || "").toLowerCase();
|
const p = (r.mode || "").toLowerCase();
|
||||||
if (p === "tcp") return "TCP";
|
if (p === "tcp") return "TCP";
|
||||||
if (p === "udp") return "UDP";
|
if (p === "udp") return "UDP";
|
||||||
return (r.protocol || "—").toUpperCase();
|
return (r.mode || "—").toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function PublicResourceMeta({ resource: r }: { resource: PublicResourceRow }) {
|
function PublicResourceMeta({ resource: r }: { resource: PublicResourceRow }) {
|
||||||
@@ -91,7 +91,7 @@ function PrivateResourceMeta({ row }: { row: SiteResourceRow }) {
|
|||||||
|
|
||||||
function PublicAccessMethod({ resource: r }: { resource: PublicResourceRow }) {
|
function PublicAccessMethod({ resource: r }: { resource: PublicResourceRow }) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
if (!r.http) {
|
if (!["http", "ssh", "rdp", "vnc"].includes(r.mode || "")) {
|
||||||
return (
|
return (
|
||||||
<CopyToClipboard
|
<CopyToClipboard
|
||||||
text={r.proxyPort?.toString() ?? ""}
|
text={r.proxyPort?.toString() ?? ""}
|
||||||
|
|||||||
Reference in New Issue
Block a user