-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.ts
More file actions
108 lines (95 loc) · 3.94 KB
/
Copy pathserver.ts
File metadata and controls
108 lines (95 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import { createServer } from "node:http";
import nextEnv from "@next/env";
import next from "next";
import { applyCanonicalEnv } from "./src/server/canonical-env";
import {
createLocalAppBoundary,
resolveLocalAppBoundaryConfig,
} from "./src/server/local-app-boundary";
const dev = process.env.NODE_ENV !== "production";
const { loadEnvConfig } = nextEnv;
// Node's --env-file has already populated canonical EH_* values. Project them
// before Next snapshots/reloads the environment so inherited legacy adapter
// variables cannot regain precedence during loadEnvConfig.
applyCanonicalEnv();
loadEnvConfig(process.cwd(), dev);
applyCanonicalEnv();
// Storage-bearing application modules must not load until the canonical app
// configuration has been projected into process.env. Importing the terminal
// gateway concurrently can reach Mastra storage first and permanently capture
// a stale saved domain override for this process.
const { applyAppConfigToProcessEnv } = await import("./src/server/app-config");
applyAppConfigToProcessEnv();
const { createLabTerminalGateway } = await import("./src/server/labs/terminal-gateway");
const appBoundary = createLocalAppBoundary(
resolveLocalAppBoundaryConfig(),
process.env.EH_APP_SESSION_CREDENTIAL,
);
const hostname = appBoundary.config.bindHost;
const port = appBoundary.config.port;
const useWebpack = process.env.NEXT_DEV_BUNDLER === "webpack";
const app = next({ dev, hostname, port, ...(useWebpack ? { webpack: true } : {}) });
const handle = app.getRequestHandler();
const terminalGateway = createLabTerminalGateway({
mode:
(process.env.PROJECT_LAB_TERMINAL_MODE as "auto" | "docker" | "local" | undefined) ?? "auto",
authorizeUpgrade: appBoundary.authorizeWebSocket,
});
await app.prepare();
const server = createServer((request, response) => {
if (!appBoundary.authorizeHttp(request, response)) return;
void handle(request, response);
});
// Node defaults requestTimeout to five minutes. A local model may spend that
// long prefilling a wide context before its first streamed token, so the HTTP
// transport must not undercut the controller's two-hour maximum run budget.
server.requestTimeout = 2 * 60 * 60 * 1000;
server.timeout = 0;
server.on("upgrade", (request, socket, head) => {
if (!appBoundary.authorizeWebSocket(request)) {
socket.destroy();
return;
}
if (terminalGateway.handleUpgrade(request, socket, head)) {
return;
}
// Leave Next.js dev/prod upgrade requests, including HMR, to Next's own
// upgrade handler registered by getRequestHandler().
});
server.listen(port, hostname, () => {
console.log(`> Ready on http://${hostname}:${port}`);
console.log("> Lab terminal WebSocket mounted on the Next.js server");
});
let shuttingDown = false;
async function shutdown(signal: "SIGINT" | "SIGTERM") {
if (shuttingDown) return;
shuttingDown = true;
console.log(`> ${signal} received; closing services and flushing observability`);
terminalGateway.close();
try {
// Resolve application modules while the Next.js dev compiler is still
// available. Importing them after app.close() races the compiler teardown
// and can fail with "TransformError: The service is no longer running".
const { mastra } = await import("./src/mastra");
await Promise.all([
new Promise<void>((resolve, reject) => {
server.close((error) => {
if (error) reject(error);
else resolve();
});
server.closeIdleConnections();
}),
app.close(),
]);
await mastra.shutdown();
// Next dev servers and storage adapters may retain background handles even
// after their documented close/shutdown promises resolve. All durable
// flushes are complete at this point, so terminate deterministically.
process.exit(0);
} catch (error) {
console.error("> Graceful shutdown failed", error);
process.exit(1);
}
}
process.once("SIGINT", () => void shutdown("SIGINT"));
process.once("SIGTERM", () => void shutdown("SIGTERM"));