mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-10 22:48:14 +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 { APP_PATH } from "@server/lib/consts";
|
||||||
import { existsSync, mkdirSync } from "fs";
|
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 location = path.join(APP_PATH, "db", "db.sqlite");
|
||||||
export const exists = checkFileExists(location);
|
export const exists = checkFileExists(location);
|
||||||
|
|
||||||
bootstrapVolume();
|
bootstrapVolume();
|
||||||
|
|
||||||
function createDb() {
|
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") {
|
if (process.env.ENABLE_SQLITE_WAL_MODE == "true") {
|
||||||
// Enable WAL mode — allows concurrent readers + single writer, preventing
|
// Enable WAL mode — allows concurrent readers + single writer, preventing
|
||||||
|
|||||||
@@ -1542,6 +1542,12 @@ export async function getTraefikConfig(
|
|||||||
aiGatewayHost = undefined;
|
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
|
// Public inference resources: same TLS/cert-resolver handling as
|
||||||
// plain http-mode resources, but the service points at the AI
|
// plain http-mode resources, but the service points at the AI
|
||||||
// gateway instead of any real backend targets.
|
// 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 =
|
const additionalMiddlewares =
|
||||||
config.getRawConfig().traefik.additional_middlewares || [];
|
config.getRawConfig().traefik.additional_middlewares || [];
|
||||||
const routerMiddlewares = [
|
const routerMiddlewares = [badgerMiddlewareName];
|
||||||
badgerMiddlewareName,
|
|
||||||
irHeadersMiddlewareName,
|
if (aiGatewayOverride) {
|
||||||
...additionalMiddlewares
|
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) {
|
if (ir.ssl) {
|
||||||
config_output.http.routers[routerName + "-redirect"] = {
|
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 =
|
const additionalMiddlewares =
|
||||||
config.getRawConfig().traefik.additional_middlewares || [];
|
config.getRawConfig().traefik.additional_middlewares || [];
|
||||||
const routerMiddlewares = [
|
const routerMiddlewares: string[] = [];
|
||||||
srHeadersMiddlewareName,
|
|
||||||
...additionalMiddlewares
|
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) {
|
if (sr.ssl) {
|
||||||
config_output.http.routers[routerName + "-redirect"] = {
|
config_output.http.routers[routerName + "-redirect"] = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
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 { db } from "@server/db";
|
||||||
import { eq, isNotNull, and } from "drizzle-orm";
|
import { eq, isNotNull, and } from "drizzle-orm";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
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) => {
|
sitesRes.map(async (site) => {
|
||||||
if (site.type === "wireguard") {
|
if (site.type === "wireguard") {
|
||||||
return {
|
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 = {
|
const configResponse: GetConfigResponse = {
|
||||||
listenPort: exitNode.listenPort || 51820,
|
listenPort: exitNode.listenPort || 51820,
|
||||||
ipAddress: exitNode.address,
|
ipAddress: exitNode.address,
|
||||||
|
|||||||
Reference in New Issue
Block a user