From 8c3d0bd7f6d943bae1077e928eed6b28cecc9fd5 Mon Sep 17 00:00:00 2001 From: kdcokenny Date: Wed, 30 Sep 2026 20:27:27 +0000 Subject: [PATCH] fix: use formatEndpoint for local targets --- server/lib/traefik/loadBalancer.test.ts | 81 +++++++++++++++++++++++++ server/lib/traefik/loadBalancer.ts | 9 +-- 2 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 server/lib/traefik/loadBalancer.test.ts diff --git a/server/lib/traefik/loadBalancer.test.ts b/server/lib/traefik/loadBalancer.test.ts new file mode 100644 index 000000000..0ffd5201d --- /dev/null +++ b/server/lib/traefik/loadBalancer.test.ts @@ -0,0 +1,81 @@ +import "@server/extendZod"; +import { assertEqualsObj } from "@test/assert"; +import { + buildHttpLoadBalancerServers, + buildTcpUdpLoadBalancerServers +} from "./loadBalancer"; +import type { TargetWithSite } from "./types"; + +function makeTarget(ip: string, siteType: string): TargetWithSite { + return { + targetId: 1, + resourceId: 1, + providerId: null, + siteId: 1, + ip, + port: 8080, + internalPort: 9000, + method: "http", + enabled: true, + health: "healthy", + path: null, + pathMatchType: null, + rewritePath: null, + rewritePathType: null, + priority: 100, + mode: "http", + authToken: null, + site: { + siteId: 1, + type: siteType, + subnet: null, + exitNodeId: null, + online: true + } + }; +} + +function runTests() { + const ipCases = [ + { ip: "2001:db8::1", host: "[2001:db8::1]" }, + { ip: "192.0.2.1", host: "192.0.2.1" } + ]; + const cases = [ + ...ipCases, + { ip: "backend.example.com", host: "backend.example.com" } + ]; + + for (const { ip, host } of cases) { + const target = makeTarget(ip, "local"); + assertEqualsObj( + buildHttpLoadBalancerServers([target]), + [{ url: `http://${host}:8080` }], + `Local HTTP target ${ip} should format its endpoint` + ); + assertEqualsObj( + buildTcpUdpLoadBalancerServers([target]), + [{ address: `${host}:8080` }], + `Local TCP/UDP target ${ip} should format its endpoint` + ); + } + + for (const { ip, host } of ipCases) { + const target = makeTarget("backend.example.com", "newt"); + target.site.subnet = `${ip}/${ip.includes(":") ? 128 : 32}`; + + assertEqualsObj( + buildHttpLoadBalancerServers([target]), + [{ url: `http://${host}:9000` }], + "Newt HTTP target should use the subnet endpoint and internal port" + ); + assertEqualsObj( + buildTcpUdpLoadBalancerServers([target]), + [{ address: `${host}:9000` }], + "Newt TCP/UDP target should use the subnet endpoint and internal port" + ); + } + + console.log("All load balancer endpoint tests passed!"); +} + +runTests(); diff --git a/server/lib/traefik/loadBalancer.ts b/server/lib/traefik/loadBalancer.ts index af1454a18..538f6bf07 100644 --- a/server/lib/traefik/loadBalancer.ts +++ b/server/lib/traefik/loadBalancer.ts @@ -1,3 +1,4 @@ +import { formatEndpoint } from "@server/lib/ip"; import { TargetWithSite } from "./types"; /** @@ -48,12 +49,12 @@ export function buildHttpLoadBalancerServers(targets: TargetWithSite[]) { target.site.type === "wireguard" ) { return { - url: `${target.method}://${target.ip}:${target.port}` + url: `${target.method}://${formatEndpoint(target.ip, target.port)}` }; } else if (target.site.type === "newt") { const ip = target.site.subnet!.split("/")[0]; return { - url: `${target.method}://${ip}:${target.internalPort}` + url: `${target.method}://${formatEndpoint(ip, target.internalPort!)}` }; } }) @@ -111,12 +112,12 @@ export function buildTcpUdpLoadBalancerServers(targets: TargetWithSite[]) { target.site.type === "wireguard" ) { return { - address: `${target.ip}:${target.port}` + address: formatEndpoint(target.ip, target.port) }; } else if (target.site.type === "newt") { const ip = target.site.subnet!.split("/")[0]; return { - address: `${ip}:${target.internalPort}` + address: formatEndpoint(ip, target.internalPort!) }; } });