Files
pangolin/server/index.ts
Josh Voyles d6abe83fdc fix: memory improvements
- SQLite: enable WAL mode and PRAGMA performance settings

- ws.ts (public + private): fix clientConfigVersions memory leak

- internal server: add rate limiting and request timeouts

- audit log: fix flush re-queue feedback loop

- memory: add monitoring instrumentation

- security: remove debug log of full request body
2026-05-02 07:37:18 -04:00

116 lines
3.4 KiB
JavaScript

#! /usr/bin/env node
import "./extendZod.ts";
import { runSetupFunctions } from "./setup";
import { createApiServer } from "./apiServer";
import { createNextServer } from "./nextServer";
import { createInternalServer } from "./internalServer";
import { createIntegrationApiServer } from "./integrationApiServer";
import {
ApiKey,
ApiKeyOrg,
RemoteExitNode,
Session,
SiteResource,
User,
UserOrg
} from "@server/db";
import config from "@server/lib/config";
import { setHostMeta } from "@server/lib/hostMeta";
import { initTelemetryClient } from "@server/lib/telemetry";
import { TraefikConfigManager } from "@server/lib/traefik/TraefikConfigManager";
import { initCleanup } from "#dynamic/cleanup";
import license from "#dynamic/license/license";
import { initLogCleanupInterval } from "@server/lib/cleanupLogs";
import { initAcmeCertSync } from "#dynamic/lib/acmeCertSync";
import { fetchServerIp } from "@server/lib/serverIpService";
import logger from "@server/logger";
/**
* Periodic memory usage logging for monitoring and leak detection.
* Logs heap usage, external (native) memory, and RSS every 60 seconds.
* This is lightweight (single process.memoryUsage() call) and provides
* the data needed to detect slow memory growth over hours/days.
*/
function startMemoryMonitor(): void {
const INTERVAL_MS = 60_000; // every 60 seconds
const timer = setInterval(() => {
const mem = process.memoryUsage();
logger.info(
`Memory usage - ` +
`heapUsed: ${(mem.heapUsed / 1024 / 1024).toFixed(1)}MB, ` +
`heapTotal: ${(mem.heapTotal / 1024 / 1024).toFixed(1)}MB, ` +
`rss: ${(mem.rss / 1024 / 1024).toFixed(1)}MB, ` +
`external: ${(mem.external / 1024 / 1024).toFixed(1)}MB, ` +
`arrayBuffers: ${(mem.arrayBuffers / 1024 / 1024).toFixed(1)}MB`
);
}, INTERVAL_MS);
timer.unref();
}
async function startServers() {
await setHostMeta();
await config.initServer();
license.setServerSecret(config.getRawConfig().server.secret!);
await license.check();
await runSetupFunctions();
await fetchServerIp();
initTelemetryClient();
initLogCleanupInterval();
initAcmeCertSync();
// Start memory monitoring for leak detection
startMemoryMonitor();
// Start all servers
const apiServer = createApiServer();
const internalServer = createInternalServer();
const nextServer = await createNextServer();
if (config.getRawConfig().traefik.file_mode) {
const monitor = new TraefikConfigManager();
await monitor.start();
}
let integrationServer;
if (config.getRawConfig().flags?.enable_integration_api) {
integrationServer = createIntegrationApiServer();
}
await initCleanup();
return {
apiServer,
nextServer,
internalServer,
integrationServer
};
}
// Types
declare global {
namespace Express {
interface Request {
apiKey?: ApiKey;
user?: User;
session: Session;
userOrg?: UserOrg;
apiKeyOrg?: ApiKeyOrg;
userOrgRoleIds?: number[];
userOrgId?: string;
userOrgIds?: string[];
remoteExitNode?: RemoteExitNode;
siteResource?: SiteResource;
orgPolicyAllowed?: boolean;
}
}
}
startServers().catch(console.error);