-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathserver.ts
More file actions
130 lines (112 loc) · 4.25 KB
/
Copy pathserver.ts
File metadata and controls
130 lines (112 loc) · 4.25 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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import "dotenv/config";
import { Request, Response } from "express";
import fs from "fs";
import path, { dirname } from "path";
import express from "express";
import http from "http";
import { createServer as createViteServer } from "vite";
import compression from "compression";
import serveStatic from "serve-static";
import { fileURLToPath } from "url";
// Local imports
import { serverApp } from "@server/app";
import Logger from "@shared/logger.js";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const isTest = process.env.NODE_ENV === "test" || !!process.env.VITE_TEST_BUILD;
const ssrLoader = async (app: express.Application, isProd: boolean) => {
const root = __dirname;
const resolve = (p: string) => path.resolve(__dirname, p);
const basePath = process.env.DROIDGROUND_BASE_PATH ?? "";
const indexProd = isProd ? fs.readFileSync(resolve("client/index.html"), "utf-8") : "";
const requestHandler = express.static(resolve("assets"));
app.use(requestHandler);
app.use(`${basePath}/assets`, requestHandler);
/**
* @type {import('vite').ViteDevServer}
*/
let vite: any;
if (!isProd) {
vite = await createViteServer({
root,
logLevel: isTest ? "error" : "info",
server: {
middlewareMode: true,
watch: {
// During tests we edit the files too fast and sometimes chokidar
// misses change events, so enforce polling for consistency
usePolling: true,
interval: 100,
},
},
appType: "custom",
});
// use vite's connect instance as middleware
app.use(vite.middlewares);
} else {
app.use(compression());
app.use(
serveStatic(resolve("dist/client"), {
index: false,
}),
);
}
app.use("*all", async (req: Request, res: Response) => {
try {
if (process.env.DROIDGROUND_BASE_PATH && !req.originalUrl.startsWith(`${process.env.DROIDGROUND_BASE_PATH}/`)) {
const newPath = `${process.env.DROIDGROUND_BASE_PATH}/`;
res.redirect(newPath);
return;
}
const url = req.originalUrl;
let template, render;
if (!isProd) {
// always read fresh template in dev
template = fs.readFileSync(resolve("index.html"), "utf-8");
template = await vite.transformIndexHtml(url, template);
render = (await vite.ssrLoadModule("/src/client/entry-server.tsx")).render;
} else {
template = indexProd;
// Production: dynamically import entry-server.js, ignore TypeScript checking
// @ts-ignore
const entryServer = await import("./server/entry-server.js");
render = entryServer.render;
}
const context = {};
const appHtml = render(url, context);
const html = template.replace(`<!--root-html-->`, appHtml);
res.status(200).set({ "Content-Type": "text/html" }).end(html);
} catch (e: any) {
!isProd && vite.ssrFixStacktrace(e);
Logger.error(e.stack);
res.status(500).end(e.stack);
}
});
};
const createServer = async (isProd = process.env.NODE_ENV === "production") => {
const app = express();
const httpServer = http.createServer(app);
/*
* Keep the Node.js event loop alive during startup.
* The ADB device observer (@yume-chan/adb) connects to the ADB server using an
* unref'd socket and only installs its own keep-alive timer *after* the initial
* device list is received. Until the HTTP server starts listening there is no
* other ref'd handle, so a slow ADB reply (common with `network` devices reached
* across containers) lets the event loop drain and the process exits cleanly with
* code 0 before setup finishes. This timer guarantees the process survives startup
* and is cleared once the server is listening.
*/
const startupKeepAlive = setInterval(() => {}, 1 << 30);
try {
await serverApp(app, httpServer);
await ssrLoader(app, isProd);
const host = process.env.DROIDGROUND_HOST || "0.0.0.0";
const port = process.env.DROIDGROUND_PORT || 4242;
httpServer.listen(Number(port), host, () => {
Logger.info(`DroidGround is running on http://${host}:${port} in ${isProd ? "production" : "development"} mode.`);
});
} finally {
clearInterval(startupKeepAlive);
}
};
createServer();