mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-06 04:31:22 +02:00
Merge branch 'aig' of https://github.com/fosrl/pangolin into aig
This commit is contained in:
@@ -6,13 +6,48 @@ import fs from "fs";
|
||||
import { APP_PATH } from "@server/lib/consts";
|
||||
import { existsSync, mkdirSync } from "fs";
|
||||
|
||||
// Temporary diagnostic trace for the random better-sqlite3 native aborts
|
||||
// (Statement::~Statement -> RemoveEnvironmentCleanupHook assertion). That
|
||||
// abort is a hard SIGABRT from native code, so it bypasses uncaughtException/
|
||||
// unhandledRejection and can outrun winston's async file transport. This
|
||||
// writes every statement text synchronously (fsync'd via appendFileSync) so
|
||||
// the statements immediately preceding a crash survive it. better-sqlite3's
|
||||
// `verbose` hook fires for BEGIN/SAVEPOINT/RELEASE/COMMIT/ROLLBACK too, since
|
||||
// those are just prepared statements under the hood - so this also lets us
|
||||
// see if two "transactions" ever overlap on the shared connection.
|
||||
// Enable with SQL_TRACE=true; remove once the crash is root-caused.
|
||||
function sqlTraceVerbose():
|
||||
| ((message: unknown, ...args: unknown[]) => void)
|
||||
| undefined {
|
||||
if (process.env.SQL_TRACE !== "true") {
|
||||
return undefined;
|
||||
}
|
||||
const traceLogDir = path.join(APP_PATH, "logs");
|
||||
if (!existsSync(traceLogDir)) {
|
||||
mkdirSync(traceLogDir, { recursive: true });
|
||||
}
|
||||
const traceLogPath = path.join(traceLogDir, "sql-trace.log");
|
||||
let seq = 0;
|
||||
return (message: unknown) => {
|
||||
seq += 1;
|
||||
const line = `${new Date().toISOString()} pid=${process.pid} #${seq} ${String(message).replace(/\s+/g, " ").trim()}\n`;
|
||||
try {
|
||||
fs.appendFileSync(traceLogPath, line);
|
||||
} catch {
|
||||
// best-effort diagnostic logging only
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const location = path.join(APP_PATH, "db", "db.sqlite");
|
||||
export const exists = checkFileExists(location);
|
||||
|
||||
bootstrapVolume();
|
||||
|
||||
function createDb() {
|
||||
const sqlite = new Database(location);
|
||||
const verbose =
|
||||
process.env.QUERY_LOGGING == "true" ? sqlTraceVerbose() : undefined;
|
||||
const sqlite = new Database(location, { verbose });
|
||||
|
||||
if (process.env.ENABLE_SQLITE_WAL_MODE == "true") {
|
||||
// Enable WAL mode — allows concurrent readers + single writer, preventing
|
||||
|
||||
@@ -1542,6 +1542,12 @@ export async function getTraefikConfig(
|
||||
aiGatewayHost = undefined;
|
||||
}
|
||||
|
||||
// The p-host smuggling above is only necessary when the AI gateway
|
||||
// is overridden to a different host than the resource's own. In the
|
||||
// default case, leave the Host header untouched so it's visible on
|
||||
// the other end.
|
||||
const aiGatewayOverride = config.getRawConfig().server.ai_gateway_override;
|
||||
|
||||
// Public inference resources: same TLS/cert-resolver handling as
|
||||
// plain http-mode resources, but the service points at the AI
|
||||
// gateway instead of any real backend targets.
|
||||
@@ -1607,23 +1613,24 @@ export async function getTraefikConfig(
|
||||
}
|
||||
}
|
||||
|
||||
const irHeadersMiddlewareName = `${irKey}-headers-middleware`;
|
||||
config_output.http.middlewares[irHeadersMiddlewareName] = {
|
||||
headers: {
|
||||
customRequestHeaders: {
|
||||
...(aiGatewayHost ? { Host: aiGatewayHost } : {}),
|
||||
"p-host": fullDomain
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const additionalMiddlewares =
|
||||
config.getRawConfig().traefik.additional_middlewares || [];
|
||||
const routerMiddlewares = [
|
||||
badgerMiddlewareName,
|
||||
irHeadersMiddlewareName,
|
||||
...additionalMiddlewares
|
||||
];
|
||||
const routerMiddlewares = [badgerMiddlewareName];
|
||||
|
||||
if (aiGatewayOverride) {
|
||||
const irHeadersMiddlewareName = `${irKey}-headers-middleware`;
|
||||
config_output.http.middlewares[irHeadersMiddlewareName] = {
|
||||
headers: {
|
||||
customRequestHeaders: {
|
||||
...(aiGatewayHost ? { Host: aiGatewayHost } : {}),
|
||||
"p-host": fullDomain
|
||||
}
|
||||
}
|
||||
};
|
||||
routerMiddlewares.push(irHeadersMiddlewareName);
|
||||
}
|
||||
|
||||
routerMiddlewares.push(...additionalMiddlewares);
|
||||
|
||||
if (ir.ssl) {
|
||||
config_output.http.routers[routerName + "-redirect"] = {
|
||||
@@ -1705,22 +1712,24 @@ export async function getTraefikConfig(
|
||||
}
|
||||
}
|
||||
|
||||
const srHeadersMiddlewareName = `${srKey}-headers-middleware`;
|
||||
config_output.http.middlewares[srHeadersMiddlewareName] = {
|
||||
headers: {
|
||||
customRequestHeaders: {
|
||||
...(aiGatewayHost ? { Host: aiGatewayHost } : {}),
|
||||
"p-host": alias
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const additionalMiddlewares =
|
||||
config.getRawConfig().traefik.additional_middlewares || [];
|
||||
const routerMiddlewares = [
|
||||
srHeadersMiddlewareName,
|
||||
...additionalMiddlewares
|
||||
];
|
||||
const routerMiddlewares: string[] = [];
|
||||
|
||||
if (aiGatewayOverride) {
|
||||
const srHeadersMiddlewareName = `${srKey}-headers-middleware`;
|
||||
config_output.http.middlewares[srHeadersMiddlewareName] = {
|
||||
headers: {
|
||||
customRequestHeaders: {
|
||||
...(aiGatewayHost ? { Host: aiGatewayHost } : {}),
|
||||
"p-host": alias
|
||||
}
|
||||
}
|
||||
};
|
||||
routerMiddlewares.push(srHeadersMiddlewareName);
|
||||
}
|
||||
|
||||
routerMiddlewares.push(...additionalMiddlewares);
|
||||
|
||||
if (sr.ssl) {
|
||||
config_output.http.routers[routerName + "-redirect"] = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { sites, exitNodes, ExitNode } from "@server/db";
|
||||
import { sites, exitNodes, ExitNode, clients } from "@server/db";
|
||||
import { db } from "@server/db";
|
||||
import { eq, isNotNull, and } from "drizzle-orm";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
@@ -93,7 +93,23 @@ export async function generateGerbilConfig(exitNode: ExitNode) {
|
||||
)
|
||||
);
|
||||
|
||||
const peers = await Promise.all(
|
||||
const clientsRes = await db
|
||||
.select()
|
||||
.from(clients)
|
||||
.where(
|
||||
and(
|
||||
eq(clients.exitNodeId, exitNode.exitNodeId),
|
||||
isNotNull(clients.pubKey),
|
||||
isNotNull(clients.exitNodeSubnet)
|
||||
)
|
||||
);
|
||||
|
||||
let peers: {
|
||||
publicKey: string | null;
|
||||
allowedIps: string[];
|
||||
}[] = [];
|
||||
|
||||
const sitePeers = await Promise.all(
|
||||
sitesRes.map(async (site) => {
|
||||
if (site.type === "wireguard") {
|
||||
return {
|
||||
@@ -113,6 +129,15 @@ export async function generateGerbilConfig(exitNode: ExitNode) {
|
||||
})
|
||||
);
|
||||
|
||||
const clientPeers = clientsRes.map((client) => {
|
||||
return {
|
||||
publicKey: client.pubKey,
|
||||
allowedIps: [client.exitNodeSubnet!]
|
||||
};
|
||||
});
|
||||
|
||||
peers = [...sitePeers, ...clientPeers];
|
||||
|
||||
const configResponse: GetConfigResponse = {
|
||||
listenPort: exitNode.listenPort || 51820,
|
||||
ipAddress: exitNode.address,
|
||||
|
||||
Reference in New Issue
Block a user