mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-04 17:56:38 +00:00
Adjust algo for showing maintence page
This commit is contained in:
@@ -19,6 +19,25 @@ import { sanitize, validatePathRewriteConfig } from "./utils";
|
|||||||
const redirectHttpsMiddlewareName = "redirect-to-https";
|
const redirectHttpsMiddlewareName = "redirect-to-https";
|
||||||
const badgerMiddlewareName = "badger";
|
const badgerMiddlewareName = "badger";
|
||||||
|
|
||||||
|
// Define extended target type with site information
|
||||||
|
type TargetWithSite = Target & {
|
||||||
|
resourceId: number;
|
||||||
|
targetId: number;
|
||||||
|
ip: string | null;
|
||||||
|
method: string | null;
|
||||||
|
port: number | null;
|
||||||
|
internalPort: number | null;
|
||||||
|
enabled: boolean;
|
||||||
|
health: string | null;
|
||||||
|
site: {
|
||||||
|
siteId: number;
|
||||||
|
type: string;
|
||||||
|
subnet: string | null;
|
||||||
|
exitNodeId: number | null;
|
||||||
|
online: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export async function getTraefikConfig(
|
export async function getTraefikConfig(
|
||||||
exitNodeId: number,
|
exitNodeId: number,
|
||||||
siteTypes: string[],
|
siteTypes: string[],
|
||||||
@@ -26,17 +45,6 @@ export async function getTraefikConfig(
|
|||||||
generateLoginPageRouters = false,
|
generateLoginPageRouters = false,
|
||||||
allowRawResources = true
|
allowRawResources = true
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
// Define extended target type with site information
|
|
||||||
type TargetWithSite = Target & {
|
|
||||||
site: {
|
|
||||||
siteId: number;
|
|
||||||
type: string;
|
|
||||||
subnet: string | null;
|
|
||||||
exitNodeId: number | null;
|
|
||||||
online: boolean;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get resources with their targets and sites in a single optimized query
|
// Get resources with their targets and sites in a single optimized query
|
||||||
// Start from sites on this exit node, then join to targets and resources
|
// Start from sites on this exit node, then join to targets and resources
|
||||||
const resourcesWithTargetsAndSites = await db
|
const resourcesWithTargetsAndSites = await db
|
||||||
@@ -104,10 +112,6 @@ export async function getTraefikConfig(
|
|||||||
eq(sites.type, "local")
|
eq(sites.type, "local")
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
or(
|
|
||||||
ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets
|
|
||||||
isNull(targetHealthCheck.hcHealth) // Include targets with no health check record
|
|
||||||
),
|
|
||||||
inArray(sites.type, siteTypes),
|
inArray(sites.type, siteTypes),
|
||||||
allowRawResources
|
allowRawResources
|
||||||
? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true
|
? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true
|
||||||
@@ -193,6 +197,7 @@ export async function getTraefikConfig(
|
|||||||
port: row.port,
|
port: row.port,
|
||||||
internalPort: row.internalPort,
|
internalPort: row.internalPort,
|
||||||
enabled: row.targetEnabled,
|
enabled: row.targetEnabled,
|
||||||
|
health: row.hcHealth,
|
||||||
site: {
|
site: {
|
||||||
siteId: row.siteId,
|
siteId: row.siteId,
|
||||||
type: row.siteType,
|
type: row.siteType,
|
||||||
@@ -222,7 +227,7 @@ export async function getTraefikConfig(
|
|||||||
|
|
||||||
// get the key and the resource
|
// get the key and the resource
|
||||||
for (const [key, resource] of resourcesMap.entries()) {
|
for (const [key, resource] of resourcesMap.entries()) {
|
||||||
const targets = resource.targets;
|
const targets = resource.targets as TargetWithSite[];
|
||||||
|
|
||||||
const routerName = `${key}-${resource.name}-router`;
|
const routerName = `${key}-${resource.name}-router`;
|
||||||
const serviceName = `${key}-${resource.name}-service`;
|
const serviceName = `${key}-${resource.name}-service`;
|
||||||
@@ -471,16 +476,20 @@ export async function getTraefikConfig(
|
|||||||
|
|
||||||
// TODO: HOW TO HANDLE ^^^^^^ BETTER
|
// TODO: HOW TO HANDLE ^^^^^^ BETTER
|
||||||
const anySitesOnline = (
|
const anySitesOnline = (
|
||||||
targets as TargetWithSite[]
|
targets
|
||||||
).some((target: TargetWithSite) => target.site.online);
|
).some((target) => target.site.online);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(targets as TargetWithSite[])
|
targets
|
||||||
.filter((target: TargetWithSite) => {
|
.filter((target) => {
|
||||||
if (!target.enabled) {
|
if (!target.enabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target.health == "unhealthy") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// If any sites are online, exclude offline sites
|
// If any sites are online, exclude offline sites
|
||||||
if (anySitesOnline && !target.site.online) {
|
if (anySitesOnline && !target.site.online) {
|
||||||
return false;
|
return false;
|
||||||
@@ -508,7 +517,7 @@ export async function getTraefikConfig(
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.map((target: TargetWithSite) => {
|
.map((target) => {
|
||||||
if (
|
if (
|
||||||
target.site.type === "local" ||
|
target.site.type === "local" ||
|
||||||
target.site.type === "wireguard"
|
target.site.type === "wireguard"
|
||||||
@@ -595,11 +604,11 @@ export async function getTraefikConfig(
|
|||||||
servers: (() => {
|
servers: (() => {
|
||||||
// Check if any sites are online
|
// Check if any sites are online
|
||||||
const anySitesOnline = (
|
const anySitesOnline = (
|
||||||
targets as TargetWithSite[]
|
targets
|
||||||
).some((target: TargetWithSite) => target.site.online);
|
).some((target) => target.site.online);
|
||||||
|
|
||||||
return (targets as TargetWithSite[])
|
return targets
|
||||||
.filter((target: TargetWithSite) => {
|
.filter((target) => {
|
||||||
if (!target.enabled) {
|
if (!target.enabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -626,7 +635,7 @@ export async function getTraefikConfig(
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.map((target: TargetWithSite) => {
|
.map((target) => {
|
||||||
if (
|
if (
|
||||||
target.site.type === "local" ||
|
target.site.type === "local" ||
|
||||||
target.site.type === "wireguard"
|
target.site.type === "wireguard"
|
||||||
|
|||||||
@@ -47,6 +47,25 @@ const redirectHttpsMiddlewareName = "redirect-to-https";
|
|||||||
const redirectToRootMiddlewareName = "redirect-to-root";
|
const redirectToRootMiddlewareName = "redirect-to-root";
|
||||||
const badgerMiddlewareName = "badger";
|
const badgerMiddlewareName = "badger";
|
||||||
|
|
||||||
|
// Define extended target type with site information
|
||||||
|
type TargetWithSite = Target & {
|
||||||
|
resourceId: number;
|
||||||
|
targetId: number;
|
||||||
|
ip: string | null;
|
||||||
|
method: string | null;
|
||||||
|
port: number | null;
|
||||||
|
internalPort: number | null;
|
||||||
|
enabled: boolean;
|
||||||
|
health: string | null;
|
||||||
|
site: {
|
||||||
|
siteId: number;
|
||||||
|
type: string;
|
||||||
|
subnet: string | null;
|
||||||
|
exitNodeId: number | null;
|
||||||
|
online: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export async function getTraefikConfig(
|
export async function getTraefikConfig(
|
||||||
exitNodeId: number,
|
exitNodeId: number,
|
||||||
siteTypes: string[],
|
siteTypes: string[],
|
||||||
@@ -54,16 +73,6 @@ export async function getTraefikConfig(
|
|||||||
generateLoginPageRouters = false,
|
generateLoginPageRouters = false,
|
||||||
allowRawResources = true
|
allowRawResources = true
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
// Define extended target type with site information
|
|
||||||
type TargetWithSite = Target & {
|
|
||||||
site: {
|
|
||||||
siteId: number;
|
|
||||||
type: string;
|
|
||||||
subnet: string | null;
|
|
||||||
exitNodeId: number | null;
|
|
||||||
online: boolean;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get resources with their targets and sites in a single optimized query
|
// Get resources with their targets and sites in a single optimized query
|
||||||
// Start from sites on this exit node, then join to targets and resources
|
// Start from sites on this exit node, then join to targets and resources
|
||||||
@@ -147,10 +156,6 @@ export async function getTraefikConfig(
|
|||||||
sql`(${build != "saas" ? 1 : 0} = 1)` // Dont allow undefined local sites in cloud
|
sql`(${build != "saas" ? 1 : 0} = 1)` // Dont allow undefined local sites in cloud
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
or(
|
|
||||||
ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets
|
|
||||||
isNull(targetHealthCheck.hcHealth) // Include targets with no health check record
|
|
||||||
),
|
|
||||||
inArray(sites.type, siteTypes),
|
inArray(sites.type, siteTypes),
|
||||||
allowRawResources
|
allowRawResources
|
||||||
? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true
|
? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true
|
||||||
@@ -233,7 +238,7 @@ export async function getTraefikConfig(
|
|||||||
maintenanceModeType: row.maintenanceModeType,
|
maintenanceModeType: row.maintenanceModeType,
|
||||||
maintenanceTitle: row.maintenanceTitle,
|
maintenanceTitle: row.maintenanceTitle,
|
||||||
maintenanceMessage: row.maintenanceMessage,
|
maintenanceMessage: row.maintenanceMessage,
|
||||||
maintenanceEstimatedTime: row.maintenanceEstimatedTime,
|
maintenanceEstimatedTime: row.maintenanceEstimatedTime
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,6 +251,7 @@ export async function getTraefikConfig(
|
|||||||
port: row.port,
|
port: row.port,
|
||||||
internalPort: row.internalPort,
|
internalPort: row.internalPort,
|
||||||
enabled: row.targetEnabled,
|
enabled: row.targetEnabled,
|
||||||
|
health: row.hcHealth,
|
||||||
site: {
|
site: {
|
||||||
siteId: row.siteId,
|
siteId: row.siteId,
|
||||||
type: row.siteType,
|
type: row.siteType,
|
||||||
@@ -291,7 +297,7 @@ export async function getTraefikConfig(
|
|||||||
|
|
||||||
// get the key and the resource
|
// get the key and the resource
|
||||||
for (const [key, resource] of resourcesMap.entries()) {
|
for (const [key, resource] of resourcesMap.entries()) {
|
||||||
const targets = resource.targets;
|
const targets = resource.targets as TargetWithSite[];
|
||||||
|
|
||||||
const routerName = `${key}-${resource.name}-router`;
|
const routerName = `${key}-${resource.name}-router`;
|
||||||
const serviceName = `${key}-${resource.name}-service`;
|
const serviceName = `${key}-${resource.name}-service`;
|
||||||
@@ -408,12 +414,12 @@ export async function getTraefikConfig(
|
|||||||
certResolver: resolverName,
|
certResolver: resolverName,
|
||||||
...(preferWildcard
|
...(preferWildcard
|
||||||
? {
|
? {
|
||||||
domains: [
|
domains: [
|
||||||
{
|
{
|
||||||
main: wildCard
|
main: wildCard
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
: {})
|
: {})
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@@ -429,21 +435,15 @@ export async function getTraefikConfig(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const availableServers = (targets as TargetWithSite[]).filter(
|
const availableServers = targets.filter(
|
||||||
(target: TargetWithSite) => {
|
(target) => {
|
||||||
if (!target.enabled) return false;
|
if (!target.enabled) return false;
|
||||||
|
|
||||||
const anySitesOnline = (targets as TargetWithSite[]).some(
|
if (!target.site.online) return false;
|
||||||
(t: TargetWithSite) => t.site.online
|
|
||||||
);
|
|
||||||
if (anySitesOnline && !target.site.online) return false;
|
|
||||||
|
|
||||||
if (target.site.type === "local" || target.site.type === "wireguard") {
|
if (target.health == "unhealthy") return false;
|
||||||
return target.ip && target.port && target.method;
|
|
||||||
} else if (target.site.type === "newt") {
|
return true;
|
||||||
return target.internalPort && target.method && target.site.subnet;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -471,8 +471,10 @@ export async function getTraefikConfig(
|
|||||||
const maintenanceRouterName = `${key}-maintenance-router`;
|
const maintenanceRouterName = `${key}-maintenance-router`;
|
||||||
const rewriteMiddlewareName = `${key}-maintenance-rewrite`;
|
const rewriteMiddlewareName = `${key}-maintenance-rewrite`;
|
||||||
|
|
||||||
const entrypointHttp = config.getRawConfig().traefik.http_entrypoint;
|
const entrypointHttp =
|
||||||
const entrypointHttps = config.getRawConfig().traefik.https_entrypoint;
|
config.getRawConfig().traefik.http_entrypoint;
|
||||||
|
const entrypointHttps =
|
||||||
|
config.getRawConfig().traefik.https_entrypoint;
|
||||||
|
|
||||||
const fullDomain = resource.fullDomain;
|
const fullDomain = resource.fullDomain;
|
||||||
const domainParts = fullDomain.split(".");
|
const domainParts = fullDomain.split(".");
|
||||||
@@ -481,11 +483,16 @@ export async function getTraefikConfig(
|
|||||||
: fullDomain;
|
: fullDomain;
|
||||||
|
|
||||||
const maintenancePort = config.getRawConfig().server.next_port;
|
const maintenancePort = config.getRawConfig().server.next_port;
|
||||||
const maintenanceHost = config.getRawConfig().server.internal_hostname;
|
const maintenanceHost =
|
||||||
|
config.getRawConfig().server.internal_hostname;
|
||||||
|
|
||||||
config_output.http.services[maintenanceServiceName] = {
|
config_output.http.services[maintenanceServiceName] = {
|
||||||
loadBalancer: {
|
loadBalancer: {
|
||||||
servers: [{ url: `http://${maintenanceHost}:${maintenancePort}` }],
|
servers: [
|
||||||
|
{
|
||||||
|
url: `http://${maintenanceHost}:${maintenancePort}`
|
||||||
|
}
|
||||||
|
],
|
||||||
passHostHeader: true
|
passHostHeader: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -503,7 +510,9 @@ export async function getTraefikConfig(
|
|||||||
};
|
};
|
||||||
|
|
||||||
config_output.http.routers[maintenanceRouterName] = {
|
config_output.http.routers[maintenanceRouterName] = {
|
||||||
entryPoints: [resource.ssl ? entrypointHttps : entrypointHttp],
|
entryPoints: [
|
||||||
|
resource.ssl ? entrypointHttps : entrypointHttp
|
||||||
|
],
|
||||||
service: maintenanceServiceName,
|
service: maintenanceServiceName,
|
||||||
middlewares: [rewriteMiddlewareName],
|
middlewares: [rewriteMiddlewareName],
|
||||||
rule: rule,
|
rule: rule,
|
||||||
@@ -512,13 +521,16 @@ export async function getTraefikConfig(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Router to allow Next.js assets to load without rewrite
|
// Router to allow Next.js assets to load without rewrite
|
||||||
config_output.http.routers[`${maintenanceRouterName}-assets`] = {
|
config_output.http.routers[`${maintenanceRouterName}-assets`] =
|
||||||
entryPoints: [resource.ssl ? entrypointHttps : entrypointHttp],
|
{
|
||||||
service: maintenanceServiceName,
|
entryPoints: [
|
||||||
rule: `Host(\`${fullDomain}\`) && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`))`,
|
resource.ssl ? entrypointHttps : entrypointHttp
|
||||||
priority: 2001,
|
],
|
||||||
...(resource.ssl ? { tls } : {})
|
service: maintenanceServiceName,
|
||||||
};
|
rule: `Host(\`${fullDomain}\`) && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`))`,
|
||||||
|
priority: 2001,
|
||||||
|
...(resource.ssl ? { tls } : {})
|
||||||
|
};
|
||||||
|
|
||||||
// logger.info(`Maintenance mode active for ${fullDomain}`);
|
// logger.info(`Maintenance mode active for ${fullDomain}`);
|
||||||
|
|
||||||
@@ -654,17 +666,21 @@ export async function getTraefikConfig(
|
|||||||
// RECEIVE BANDWIDTH ENDPOINT.
|
// RECEIVE BANDWIDTH ENDPOINT.
|
||||||
|
|
||||||
// TODO: HOW TO HANDLE ^^^^^^ BETTER
|
// TODO: HOW TO HANDLE ^^^^^^ BETTER
|
||||||
const anySitesOnline = (
|
const anySitesOnline = targets.some(
|
||||||
targets as TargetWithSite[]
|
(target) => target.site.online
|
||||||
).some((target: TargetWithSite) => target.site.online);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(targets as TargetWithSite[])
|
targets
|
||||||
.filter((target: TargetWithSite) => {
|
.filter((target) => {
|
||||||
if (!target.enabled) {
|
if (!target.enabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target.health == "unhealthy") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// If any sites are online, exclude offline sites
|
// If any sites are online, exclude offline sites
|
||||||
if (anySitesOnline && !target.site.online) {
|
if (anySitesOnline && !target.site.online) {
|
||||||
return false;
|
return false;
|
||||||
@@ -692,7 +708,7 @@ export async function getTraefikConfig(
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.map((target: TargetWithSite) => {
|
.map((target) => {
|
||||||
if (
|
if (
|
||||||
target.site.type === "local" ||
|
target.site.type === "local" ||
|
||||||
target.site.type === "wireguard"
|
target.site.type === "wireguard"
|
||||||
@@ -719,14 +735,14 @@ export async function getTraefikConfig(
|
|||||||
})(),
|
})(),
|
||||||
...(resource.stickySession
|
...(resource.stickySession
|
||||||
? {
|
? {
|
||||||
sticky: {
|
sticky: {
|
||||||
cookie: {
|
cookie: {
|
||||||
name: "p_sticky", // TODO: make this configurable via config.yml like other cookies
|
name: "p_sticky", // TODO: make this configurable via config.yml like other cookies
|
||||||
secure: resource.ssl,
|
secure: resource.ssl,
|
||||||
httpOnly: true
|
httpOnly: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: {})
|
: {})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -779,11 +795,11 @@ export async function getTraefikConfig(
|
|||||||
servers: (() => {
|
servers: (() => {
|
||||||
// Check if any sites are online
|
// Check if any sites are online
|
||||||
const anySitesOnline = (
|
const anySitesOnline = (
|
||||||
targets as TargetWithSite[]
|
targets
|
||||||
).some((target: TargetWithSite) => target.site.online);
|
).some((target) => target.site.online);
|
||||||
|
|
||||||
return (targets as TargetWithSite[])
|
return targets
|
||||||
.filter((target: TargetWithSite) => {
|
.filter((target) => {
|
||||||
if (!target.enabled) {
|
if (!target.enabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -810,7 +826,7 @@ export async function getTraefikConfig(
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.map((target: TargetWithSite) => {
|
.map((target) => {
|
||||||
if (
|
if (
|
||||||
target.site.type === "local" ||
|
target.site.type === "local" ||
|
||||||
target.site.type === "wireguard"
|
target.site.type === "wireguard"
|
||||||
@@ -829,18 +845,18 @@ export async function getTraefikConfig(
|
|||||||
})(),
|
})(),
|
||||||
...(resource.proxyProtocol && protocol == "tcp" // proxy protocol only works for tcp
|
...(resource.proxyProtocol && protocol == "tcp" // proxy protocol only works for tcp
|
||||||
? {
|
? {
|
||||||
serversTransport: `${ppPrefix}${resource.proxyProtocolVersion || 1}@file` // TODO: does @file here cause issues?
|
serversTransport: `${ppPrefix}${resource.proxyProtocolVersion || 1}@file` // TODO: does @file here cause issues?
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(resource.stickySession
|
...(resource.stickySession
|
||||||
? {
|
? {
|
||||||
sticky: {
|
sticky: {
|
||||||
ipStrategy: {
|
ipStrategy: {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
sourcePort: true
|
sourcePort: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: {})
|
: {})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -888,9 +904,10 @@ export async function getTraefikConfig(
|
|||||||
loadBalancer: {
|
loadBalancer: {
|
||||||
servers: [
|
servers: [
|
||||||
{
|
{
|
||||||
url: `http://${config.getRawConfig().server
|
url: `http://${
|
||||||
.internal_hostname
|
config.getRawConfig().server
|
||||||
}:${config.getRawConfig().server.next_port}`
|
.internal_hostname
|
||||||
|
}:${config.getRawConfig().server.next_port}`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user