diff --git a/server/lib/traefik/getTraefikConfig.ts b/server/lib/traefik/getTraefikConfig.ts index 4aea2bff..484eaaf5 100644 --- a/server/lib/traefik/getTraefikConfig.ts +++ b/server/lib/traefik/getTraefikConfig.ts @@ -19,6 +19,25 @@ import { sanitize, validatePathRewriteConfig } from "./utils"; const redirectHttpsMiddlewareName = "redirect-to-https"; 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( exitNodeId: number, siteTypes: string[], @@ -26,17 +45,6 @@ export async function getTraefikConfig( generateLoginPageRouters = false, allowRawResources = true ): Promise { - // 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 // Start from sites on this exit node, then join to targets and resources const resourcesWithTargetsAndSites = await db @@ -104,10 +112,6 @@ export async function getTraefikConfig( 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), allowRawResources ? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true @@ -193,6 +197,7 @@ export async function getTraefikConfig( port: row.port, internalPort: row.internalPort, enabled: row.targetEnabled, + health: row.hcHealth, site: { siteId: row.siteId, type: row.siteType, @@ -222,7 +227,7 @@ export async function getTraefikConfig( // get the key and the resource for (const [key, resource] of resourcesMap.entries()) { - const targets = resource.targets; + const targets = resource.targets as TargetWithSite[]; const routerName = `${key}-${resource.name}-router`; const serviceName = `${key}-${resource.name}-service`; @@ -471,16 +476,20 @@ export async function getTraefikConfig( // TODO: HOW TO HANDLE ^^^^^^ BETTER const anySitesOnline = ( - targets as TargetWithSite[] - ).some((target: TargetWithSite) => target.site.online); + targets + ).some((target) => target.site.online); return ( - (targets as TargetWithSite[]) - .filter((target: TargetWithSite) => { + targets + .filter((target) => { if (!target.enabled) { return false; } + if (target.health == "unhealthy") { + return false; + } + // If any sites are online, exclude offline sites if (anySitesOnline && !target.site.online) { return false; @@ -508,7 +517,7 @@ export async function getTraefikConfig( } return true; }) - .map((target: TargetWithSite) => { + .map((target) => { if ( target.site.type === "local" || target.site.type === "wireguard" @@ -595,11 +604,11 @@ export async function getTraefikConfig( servers: (() => { // Check if any sites are online const anySitesOnline = ( - targets as TargetWithSite[] - ).some((target: TargetWithSite) => target.site.online); + targets + ).some((target) => target.site.online); - return (targets as TargetWithSite[]) - .filter((target: TargetWithSite) => { + return targets + .filter((target) => { if (!target.enabled) { return false; } @@ -626,7 +635,7 @@ export async function getTraefikConfig( } return true; }) - .map((target: TargetWithSite) => { + .map((target) => { if ( target.site.type === "local" || target.site.type === "wireguard" diff --git a/server/private/lib/traefik/getTraefikConfig.ts b/server/private/lib/traefik/getTraefikConfig.ts index 5a359d36..99e5b39c 100644 --- a/server/private/lib/traefik/getTraefikConfig.ts +++ b/server/private/lib/traefik/getTraefikConfig.ts @@ -47,6 +47,25 @@ const redirectHttpsMiddlewareName = "redirect-to-https"; const redirectToRootMiddlewareName = "redirect-to-root"; 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( exitNodeId: number, siteTypes: string[], @@ -54,16 +73,6 @@ export async function getTraefikConfig( generateLoginPageRouters = false, allowRawResources = true ): Promise { - // 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 // 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 ) ), - or( - ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets - isNull(targetHealthCheck.hcHealth) // Include targets with no health check record - ), inArray(sites.type, siteTypes), allowRawResources ? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true @@ -233,7 +238,7 @@ export async function getTraefikConfig( maintenanceModeType: row.maintenanceModeType, maintenanceTitle: row.maintenanceTitle, maintenanceMessage: row.maintenanceMessage, - maintenanceEstimatedTime: row.maintenanceEstimatedTime, + maintenanceEstimatedTime: row.maintenanceEstimatedTime }); } @@ -246,6 +251,7 @@ export async function getTraefikConfig( port: row.port, internalPort: row.internalPort, enabled: row.targetEnabled, + health: row.hcHealth, site: { siteId: row.siteId, type: row.siteType, @@ -291,7 +297,7 @@ export async function getTraefikConfig( // get the key and the resource for (const [key, resource] of resourcesMap.entries()) { - const targets = resource.targets; + const targets = resource.targets as TargetWithSite[]; const routerName = `${key}-${resource.name}-router`; const serviceName = `${key}-${resource.name}-service`; @@ -408,12 +414,12 @@ export async function getTraefikConfig( certResolver: resolverName, ...(preferWildcard ? { - domains: [ - { - main: wildCard - } - ] - } + domains: [ + { + main: wildCard + } + ] + } : {}) }; } else { @@ -429,21 +435,15 @@ export async function getTraefikConfig( } } - const availableServers = (targets as TargetWithSite[]).filter( - (target: TargetWithSite) => { + const availableServers = targets.filter( + (target) => { if (!target.enabled) return false; - const anySitesOnline = (targets as TargetWithSite[]).some( - (t: TargetWithSite) => t.site.online - ); - if (anySitesOnline && !target.site.online) return false; + if (!target.site.online) return false; - if (target.site.type === "local" || target.site.type === "wireguard") { - return target.ip && target.port && target.method; - } else if (target.site.type === "newt") { - return target.internalPort && target.method && target.site.subnet; - } - return false; + if (target.health == "unhealthy") return false; + + return true; } ); @@ -471,8 +471,10 @@ export async function getTraefikConfig( const maintenanceRouterName = `${key}-maintenance-router`; const rewriteMiddlewareName = `${key}-maintenance-rewrite`; - const entrypointHttp = config.getRawConfig().traefik.http_entrypoint; - const entrypointHttps = config.getRawConfig().traefik.https_entrypoint; + const entrypointHttp = + config.getRawConfig().traefik.http_entrypoint; + const entrypointHttps = + config.getRawConfig().traefik.https_entrypoint; const fullDomain = resource.fullDomain; const domainParts = fullDomain.split("."); @@ -481,11 +483,16 @@ export async function getTraefikConfig( : fullDomain; 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] = { loadBalancer: { - servers: [{ url: `http://${maintenanceHost}:${maintenancePort}` }], + servers: [ + { + url: `http://${maintenanceHost}:${maintenancePort}` + } + ], passHostHeader: true } }; @@ -503,7 +510,9 @@ export async function getTraefikConfig( }; config_output.http.routers[maintenanceRouterName] = { - entryPoints: [resource.ssl ? entrypointHttps : entrypointHttp], + entryPoints: [ + resource.ssl ? entrypointHttps : entrypointHttp + ], service: maintenanceServiceName, middlewares: [rewriteMiddlewareName], rule: rule, @@ -512,13 +521,16 @@ export async function getTraefikConfig( }; // Router to allow Next.js assets to load without rewrite - config_output.http.routers[`${maintenanceRouterName}-assets`] = { - entryPoints: [resource.ssl ? entrypointHttps : entrypointHttp], - service: maintenanceServiceName, - rule: `Host(\`${fullDomain}\`) && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`))`, - priority: 2001, - ...(resource.ssl ? { tls } : {}) - }; + config_output.http.routers[`${maintenanceRouterName}-assets`] = + { + entryPoints: [ + resource.ssl ? entrypointHttps : entrypointHttp + ], + service: maintenanceServiceName, + rule: `Host(\`${fullDomain}\`) && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`))`, + priority: 2001, + ...(resource.ssl ? { tls } : {}) + }; // logger.info(`Maintenance mode active for ${fullDomain}`); @@ -654,17 +666,21 @@ export async function getTraefikConfig( // RECEIVE BANDWIDTH ENDPOINT. // TODO: HOW TO HANDLE ^^^^^^ BETTER - const anySitesOnline = ( - targets as TargetWithSite[] - ).some((target: TargetWithSite) => target.site.online); + const anySitesOnline = targets.some( + (target) => target.site.online + ); return ( - (targets as TargetWithSite[]) - .filter((target: TargetWithSite) => { + targets + .filter((target) => { if (!target.enabled) { return false; } + if (target.health == "unhealthy") { + return false; + } + // If any sites are online, exclude offline sites if (anySitesOnline && !target.site.online) { return false; @@ -692,7 +708,7 @@ export async function getTraefikConfig( } return true; }) - .map((target: TargetWithSite) => { + .map((target) => { if ( target.site.type === "local" || target.site.type === "wireguard" @@ -719,14 +735,14 @@ export async function getTraefikConfig( })(), ...(resource.stickySession ? { - sticky: { - cookie: { - name: "p_sticky", // TODO: make this configurable via config.yml like other cookies - secure: resource.ssl, - httpOnly: true - } - } - } + sticky: { + cookie: { + name: "p_sticky", // TODO: make this configurable via config.yml like other cookies + secure: resource.ssl, + httpOnly: true + } + } + } : {}) } }; @@ -779,11 +795,11 @@ export async function getTraefikConfig( servers: (() => { // Check if any sites are online const anySitesOnline = ( - targets as TargetWithSite[] - ).some((target: TargetWithSite) => target.site.online); + targets + ).some((target) => target.site.online); - return (targets as TargetWithSite[]) - .filter((target: TargetWithSite) => { + return targets + .filter((target) => { if (!target.enabled) { return false; } @@ -810,7 +826,7 @@ export async function getTraefikConfig( } return true; }) - .map((target: TargetWithSite) => { + .map((target) => { if ( target.site.type === "local" || target.site.type === "wireguard" @@ -829,18 +845,18 @@ export async function getTraefikConfig( })(), ...(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 ? { - sticky: { - ipStrategy: { - depth: 0, - sourcePort: true - } - } - } + sticky: { + ipStrategy: { + depth: 0, + sourcePort: true + } + } + } : {}) } }; @@ -888,9 +904,10 @@ export async function getTraefikConfig( loadBalancer: { servers: [ { - url: `http://${config.getRawConfig().server - .internal_hostname - }:${config.getRawConfig().server.next_port}` + url: `http://${ + config.getRawConfig().server + .internal_hostname + }:${config.getRawConfig().server.next_port}` } ] }