mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-08 05:56:38 +00:00
Fix various bugs
This commit is contained in:
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user