From 46807c6477ca16ce0407d802fc33eba21ad3e60c Mon Sep 17 00:00:00 2001 From: Owen Date: Thu, 16 Oct 2025 10:23:09 -0700 Subject: [PATCH] Fix various bugs --- docker-compose.example.yml | 2 +- install/config/docker-compose.yml | 2 +- server/lib/traefik/getTraefikConfig.ts | 20 ++- .../private/lib/traefik/getTraefikConfig.ts | 24 +++- .../private/routers/gerbil/createExitNode.ts | 9 +- server/routers/target/createTarget.ts | 116 ++++++++++-------- .../resources/[niceId]/proxy/page.tsx | 2 +- .../settings/resources/create/page.tsx | 8 +- 8 files changed, 116 insertions(+), 67 deletions(-) diff --git a/docker-compose.example.yml b/docker-compose.example.yml index 28097f32..21a5134f 100644 --- a/docker-compose.example.yml +++ b/docker-compose.example.yml @@ -20,7 +20,7 @@ services: pangolin: condition: service_healthy command: - - --reachableAt=http://gerbil:3003 + - --reachableAt=http://gerbil:3004 - --generateAndSaveKeyTo=/var/config/key - --remoteConfig=http://pangolin:3001/api/v1/ volumes: diff --git a/install/config/docker-compose.yml b/install/config/docker-compose.yml index 37ec2454..b507e914 100644 --- a/install/config/docker-compose.yml +++ b/install/config/docker-compose.yml @@ -20,7 +20,7 @@ services: pangolin: condition: service_healthy command: - - --reachableAt=http://gerbil:3003 + - --reachableAt=http://gerbil:3004 - --generateAndSaveKeyTo=/var/config/key - --remoteConfig=http://pangolin:3001/api/v1/ volumes: diff --git a/server/lib/traefik/getTraefikConfig.ts b/server/lib/traefik/getTraefikConfig.ts index 4055c7a0..cf4d5d42 100644 --- a/server/lib/traefik/getTraefikConfig.ts +++ b/server/lib/traefik/getTraefikConfig.ts @@ -1,5 +1,15 @@ import { db, targetHealthCheck } from "@server/db"; -import { and, eq, inArray, or, isNull, ne, isNotNull, desc } from "drizzle-orm"; +import { + and, + eq, + inArray, + or, + isNull, + ne, + isNotNull, + desc, + sql +} from "drizzle-orm"; import logger from "@server/logger"; import config from "@server/lib/config"; import { resources, sites, Target, targets } from "@server/db"; @@ -78,7 +88,13 @@ export async function getTraefikConfig( and( eq(targets.enabled, true), eq(resources.enabled, true), - or(eq(sites.exitNodeId, exitNodeId), isNull(sites.exitNodeId)), + or( + eq(sites.exitNodeId, exitNodeId), + and( + isNull(sites.exitNodeId), + sql`(${siteTypes.includes("local") ? 1 : 0} = 1)` // only allow local sites if "local" is in siteTypes + ) + ), or( ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets isNull(targetHealthCheck.hcHealth) // Include targets with no health check record diff --git a/server/private/lib/traefik/getTraefikConfig.ts b/server/private/lib/traefik/getTraefikConfig.ts index 85a0589d..9408dbfd 100644 --- a/server/private/lib/traefik/getTraefikConfig.ts +++ b/server/private/lib/traefik/getTraefikConfig.ts @@ -19,7 +19,17 @@ import { loginPage, targetHealthCheck } from "@server/db"; -import { and, eq, inArray, or, isNull, ne, isNotNull, desc } from "drizzle-orm"; +import { + and, + eq, + inArray, + or, + isNull, + ne, + isNotNull, + desc, + sql +} from "drizzle-orm"; import logger from "@server/logger"; import config from "@server/lib/config"; import { orgs, resources, sites, Target, targets } from "@server/db"; @@ -110,15 +120,19 @@ export async function getTraefikConfig( and( eq(targets.enabled, true), eq(resources.enabled, true), - // or( - eq(sites.exitNodeId, exitNodeId), - // isNull(sites.exitNodeId) - // ), + or( + eq(sites.exitNodeId, exitNodeId), + and( + isNull(sites.exitNodeId), + sql`(${siteTypes.includes("local") ? 1 : 0} = 1)` // only allow local sites if "local" is in siteTypes + ) + ), or( ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets isNull(targetHealthCheck.hcHealth) // Include targets with no health check record ), inArray(sites.type, siteTypes), + // lets rewrite this using sql config.getRawConfig().traefik.allow_raw_resources ? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true : eq(resources.http, true) diff --git a/server/private/routers/gerbil/createExitNode.ts b/server/private/routers/gerbil/createExitNode.ts index 9577d8e9..11754f1e 100644 --- a/server/private/routers/gerbil/createExitNode.ts +++ b/server/private/routers/gerbil/createExitNode.ts @@ -61,7 +61,14 @@ export async function createExitNode( `Created new exit node ${exitNode.name} with address ${exitNode.address} and port ${exitNode.listenPort}` ); } else { - exitNode = exitNodeQuery; + // update the reachable at + [exitNode] = await db + .update(exitNodes) + .set({ + reachableAt + }) + .where(eq(exitNodes.exitNodeId, exitNodeQuery.exitNodeId)) + .returning(); } return exitNode; diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index 73e21521..0752c93c 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -46,14 +46,23 @@ const createTargetSchema = z .optional() .nullable(), hcTimeout: z.number().int().positive().min(1).optional().nullable(), - hcHeaders: z.array(z.object({ name: z.string(), value: z.string() })).nullable().optional(), + hcHeaders: z + .array(z.object({ name: z.string(), value: z.string() })) + .nullable() + .optional(), hcFollowRedirects: z.boolean().optional().nullable(), hcMethod: z.string().min(1).optional().nullable(), hcStatus: z.number().int().optional().nullable(), path: z.string().optional().nullable(), - pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), + pathMatchType: z + .enum(["exact", "prefix", "regex"]) + .optional() + .nullable(), rewritePath: z.string().optional().nullable(), - rewritePathType: z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(), + rewritePathType: z + .enum(["exact", "prefix", "regex", "stripPrefix"]) + .optional() + .nullable(), priority: z.number().int().min(1).max(1000) }) .strict(); @@ -164,6 +173,7 @@ export async function createTarget( let newTarget: Target[] = []; let healthCheck: TargetHealthCheck[] = []; + let targetIps: string[] = []; if (site.type == "local") { newTarget = await db .insert(targets) @@ -186,7 +196,7 @@ export async function createTarget( ); } - const { internalPort, targetIps } = await pickPort( + const { internalPort, targetIps: newTargetIps } = await pickPort( site.siteId!, db ); @@ -218,57 +228,59 @@ export async function createTarget( }) .returning(); - let hcHeaders = null; - if (targetData.hcHeaders) { - hcHeaders = JSON.stringify(targetData.hcHeaders); - } - - healthCheck = await db - .insert(targetHealthCheck) - .values({ - targetId: newTarget[0].targetId, - hcEnabled: targetData.hcEnabled ?? false, - hcPath: targetData.hcPath ?? null, - hcScheme: targetData.hcScheme ?? null, - hcMode: targetData.hcMode ?? null, - hcHostname: targetData.hcHostname ?? null, - hcPort: targetData.hcPort ?? null, - hcInterval: targetData.hcInterval ?? null, - hcUnhealthyInterval: targetData.hcUnhealthyInterval ?? null, - hcTimeout: targetData.hcTimeout ?? null, - hcHeaders: hcHeaders, - hcFollowRedirects: targetData.hcFollowRedirects ?? null, - hcMethod: targetData.hcMethod ?? null, - hcStatus: targetData.hcStatus ?? null, - hcHealth: "unknown" - }) - .returning(); - // add the new target to the targetIps array - targetIps.push(`${targetData.ip}/32`); + newTargetIps.push(`${targetData.ip}/32`); - if (site.pubKey) { - if (site.type == "wireguard") { - await addPeer(site.exitNodeId!, { - publicKey: site.pubKey, - allowedIps: targetIps.flat() - }); - } else if (site.type == "newt") { - // get the newt on the site by querying the newt table for siteId - const [newt] = await db - .select() - .from(newts) - .where(eq(newts.siteId, site.siteId)) - .limit(1); + targetIps = newTargetIps; + } - await addTargets( - newt.newtId, - newTarget, - healthCheck, - resource.protocol, - resource.proxyPort - ); - } + let hcHeaders = null; + if (targetData.hcHeaders) { + hcHeaders = JSON.stringify(targetData.hcHeaders); + } + + healthCheck = await db + .insert(targetHealthCheck) + .values({ + targetId: newTarget[0].targetId, + hcEnabled: targetData.hcEnabled ?? false, + hcPath: targetData.hcPath ?? null, + hcScheme: targetData.hcScheme ?? null, + hcMode: targetData.hcMode ?? null, + hcHostname: targetData.hcHostname ?? null, + hcPort: targetData.hcPort ?? null, + hcInterval: targetData.hcInterval ?? null, + hcUnhealthyInterval: targetData.hcUnhealthyInterval ?? null, + hcTimeout: targetData.hcTimeout ?? null, + hcHeaders: hcHeaders, + hcFollowRedirects: targetData.hcFollowRedirects ?? null, + hcMethod: targetData.hcMethod ?? null, + hcStatus: targetData.hcStatus ?? null, + hcHealth: "unknown" + }) + .returning(); + + if (site.pubKey) { + if (site.type == "wireguard") { + await addPeer(site.exitNodeId!, { + publicKey: site.pubKey, + allowedIps: targetIps.flat() + }); + } else if (site.type == "newt") { + // get the newt on the site by querying the newt table for siteId + const [newt] = await db + .select() + .from(newts) + .where(eq(newts.siteId, site.siteId)) + .limit(1); + + await addTargets( + newt.newtId, + newTarget, + healthCheck, + resource.protocol, + resource.proxyPort + ); } } diff --git a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx index a399a6fa..e3ab794b 100644 --- a/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[niceId]/proxy/page.tsx @@ -666,7 +666,7 @@ export default function ReverseProxyTargets(props: { ...target, ...data, updated: true, - // siteType: site?.type || null + siteType: site ? site.type : target.siteType } : target ) diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/create/page.tsx index 04103a13..759d0208 100644 --- a/src/app/[orgId]/settings/resources/create/page.tsx +++ b/src/app/[orgId]/settings/resources/create/page.tsx @@ -375,8 +375,8 @@ export default function Page() { // Helper function to check if all targets have required fields using schema validation const areAllTargetsValid = () => { if (targets.length === 0) return true; // No targets is valid - - return targets.every(target => { + + return targets.every((target) => { try { addTargetSchema.parse({ ip: target.ip, @@ -518,8 +518,8 @@ export default function Page() { ? { ...target, ...data, - updated: true - // siteType: site?.type || null + updated: true, + siteType: site ? site.type : target.siteType } : target )