Fix various bugs

This commit is contained in:
Owen
2025-10-16 10:23:09 -07:00
parent b578786e62
commit 46807c6477
8 changed files with 116 additions and 67 deletions

View File

@@ -20,7 +20,7 @@ services:
pangolin: pangolin:
condition: service_healthy condition: service_healthy
command: command:
- --reachableAt=http://gerbil:3003 - --reachableAt=http://gerbil:3004
- --generateAndSaveKeyTo=/var/config/key - --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/ - --remoteConfig=http://pangolin:3001/api/v1/
volumes: volumes:

View File

@@ -20,7 +20,7 @@ services:
pangolin: pangolin:
condition: service_healthy condition: service_healthy
command: command:
- --reachableAt=http://gerbil:3003 - --reachableAt=http://gerbil:3004
- --generateAndSaveKeyTo=/var/config/key - --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/ - --remoteConfig=http://pangolin:3001/api/v1/
volumes: volumes:

View File

@@ -1,5 +1,15 @@
import { db, targetHealthCheck } from "@server/db"; 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 logger from "@server/logger";
import config from "@server/lib/config"; import config from "@server/lib/config";
import { resources, sites, Target, targets } from "@server/db"; import { resources, sites, Target, targets } from "@server/db";
@@ -78,7 +88,13 @@ export async function getTraefikConfig(
and( and(
eq(targets.enabled, true), eq(targets.enabled, true),
eq(resources.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( or(
ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets
isNull(targetHealthCheck.hcHealth) // Include targets with no health check record isNull(targetHealthCheck.hcHealth) // Include targets with no health check record

View File

@@ -19,7 +19,17 @@ import {
loginPage, loginPage,
targetHealthCheck targetHealthCheck
} from "@server/db"; } 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 logger from "@server/logger";
import config from "@server/lib/config"; import config from "@server/lib/config";
import { orgs, resources, sites, Target, targets } from "@server/db"; import { orgs, resources, sites, Target, targets } from "@server/db";
@@ -110,15 +120,19 @@ export async function getTraefikConfig(
and( and(
eq(targets.enabled, true), eq(targets.enabled, true),
eq(resources.enabled, true), eq(resources.enabled, true),
// or( or(
eq(sites.exitNodeId, exitNodeId), eq(sites.exitNodeId, exitNodeId),
// isNull(sites.exitNodeId) and(
// ), isNull(sites.exitNodeId),
sql`(${siteTypes.includes("local") ? 1 : 0} = 1)` // only allow local sites if "local" is in siteTypes
)
),
or( or(
ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets
isNull(targetHealthCheck.hcHealth) // Include targets with no health check record isNull(targetHealthCheck.hcHealth) // Include targets with no health check record
), ),
inArray(sites.type, siteTypes), inArray(sites.type, siteTypes),
// lets rewrite this using sql
config.getRawConfig().traefik.allow_raw_resources config.getRawConfig().traefik.allow_raw_resources
? 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
: eq(resources.http, true) : eq(resources.http, true)

View File

@@ -61,7 +61,14 @@ export async function createExitNode(
`Created new exit node ${exitNode.name} with address ${exitNode.address} and port ${exitNode.listenPort}` `Created new exit node ${exitNode.name} with address ${exitNode.address} and port ${exitNode.listenPort}`
); );
} else { } else {
exitNode = exitNodeQuery; // update the reachable at
[exitNode] = await db
.update(exitNodes)
.set({
reachableAt
})
.where(eq(exitNodes.exitNodeId, exitNodeQuery.exitNodeId))
.returning();
} }
return exitNode; return exitNode;

View File

@@ -46,14 +46,23 @@ const createTargetSchema = z
.optional() .optional()
.nullable(), .nullable(),
hcTimeout: z.number().int().positive().min(1).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(), hcFollowRedirects: z.boolean().optional().nullable(),
hcMethod: z.string().min(1).optional().nullable(), hcMethod: z.string().min(1).optional().nullable(),
hcStatus: z.number().int().optional().nullable(), hcStatus: z.number().int().optional().nullable(),
path: z.string().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(), 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) priority: z.number().int().min(1).max(1000)
}) })
.strict(); .strict();
@@ -164,6 +173,7 @@ export async function createTarget(
let newTarget: Target[] = []; let newTarget: Target[] = [];
let healthCheck: TargetHealthCheck[] = []; let healthCheck: TargetHealthCheck[] = [];
let targetIps: string[] = [];
if (site.type == "local") { if (site.type == "local") {
newTarget = await db newTarget = await db
.insert(targets) .insert(targets)
@@ -186,7 +196,7 @@ export async function createTarget(
); );
} }
const { internalPort, targetIps } = await pickPort( const { internalPort, targetIps: newTargetIps } = await pickPort(
site.siteId!, site.siteId!,
db db
); );
@@ -218,57 +228,59 @@ export async function createTarget(
}) })
.returning(); .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 // add the new target to the targetIps array
targetIps.push(`${targetData.ip}/32`); newTargetIps.push(`${targetData.ip}/32`);
if (site.pubKey) { targetIps = newTargetIps;
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( let hcHeaders = null;
newt.newtId, if (targetData.hcHeaders) {
newTarget, hcHeaders = JSON.stringify(targetData.hcHeaders);
healthCheck, }
resource.protocol,
resource.proxyPort 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
);
} }
} }

View File

@@ -666,7 +666,7 @@ export default function ReverseProxyTargets(props: {
...target, ...target,
...data, ...data,
updated: true, updated: true,
// siteType: site?.type || null siteType: site ? site.type : target.siteType
} }
: target : target
) )

View File

@@ -375,8 +375,8 @@ export default function Page() {
// Helper function to check if all targets have required fields using schema validation // Helper function to check if all targets have required fields using schema validation
const areAllTargetsValid = () => { const areAllTargetsValid = () => {
if (targets.length === 0) return true; // No targets is valid if (targets.length === 0) return true; // No targets is valid
return targets.every(target => { return targets.every((target) => {
try { try {
addTargetSchema.parse({ addTargetSchema.parse({
ip: target.ip, ip: target.ip,
@@ -518,8 +518,8 @@ export default function Page() {
? { ? {
...target, ...target,
...data, ...data,
updated: true updated: true,
// siteType: site?.type || null siteType: site ? site.type : target.siteType
} }
: target : target
) )