Node flags legacy url.parse() (DEP0169) as having security implications and recommends the WHATWG URL API instead. The main request handler's parsedUrl argument to Next's handle() is optional and unused by us, so that call is dropped entirely (matching Next's own minimal custom-server example); the WS upgrade path-check now uses `new URL()` instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
import "./src/bootstrap/async-local-storage-polyfill";
|
|
import { createServer } from "node:http";
|
|
import next from "next";
|
|
import { WebSocketServer } from "ws";
|
|
import { getConfig } from "./src/lib/config/load";
|
|
import { migrateOnBoot } from "./src/lib/db/client";
|
|
import { syncAuthFromConfig } from "./src/lib/auth/sync";
|
|
import { reconcileOrphanedRuns } from "./src/lib/runner/engine";
|
|
import { killAllRuns } from "./src/lib/runner/registry";
|
|
import { attachWsServer, authenticateUpgrade } from "./src/lib/ws/server";
|
|
|
|
const dev = process.env.NODE_ENV !== "production";
|
|
const { config } = getConfig();
|
|
|
|
const port = Number(process.env.PORT ?? config.server.port);
|
|
const hostname = process.env.HOST ?? config.server.host;
|
|
|
|
migrateOnBoot();
|
|
syncAuthFromConfig();
|
|
reconcileOrphanedRuns();
|
|
|
|
const app = next({ dev, hostname, port });
|
|
const handle = app.getRequestHandler();
|
|
|
|
app.prepare().then(() => {
|
|
const httpServer = createServer((req, res) => {
|
|
handle(req, res);
|
|
});
|
|
|
|
const wss = new WebSocketServer({ noServer: true });
|
|
attachWsServer(wss);
|
|
|
|
httpServer.on("upgrade", (req, socket, head) => {
|
|
const { pathname } = new URL(req.url ?? "/", "http://internal");
|
|
if (pathname !== "/ws/runs") {
|
|
socket.destroy();
|
|
return;
|
|
}
|
|
|
|
authenticateUpgrade(req)
|
|
.then((ok) => {
|
|
if (!ok) {
|
|
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
|
|
socket.destroy();
|
|
return;
|
|
}
|
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
wss.emit("connection", ws, req);
|
|
});
|
|
})
|
|
.catch(() => socket.destroy());
|
|
});
|
|
|
|
httpServer.listen(port, hostname, () => {
|
|
console.log(
|
|
`> triggershell ready on http://${hostname}:${port} (${dev ? "development" : "production"})`,
|
|
);
|
|
});
|
|
|
|
const shutdown = (signal: string) => {
|
|
console.log(`> received ${signal}, shutting down...`);
|
|
killAllRuns();
|
|
httpServer.close(() => process.exit(0));
|
|
// Force-exit if graceful shutdown hangs (e.g. a stuck WS connection).
|
|
setTimeout(() => process.exit(1), 5000).unref();
|
|
};
|
|
|
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
});
|