chatapp-h5/h5/app-invite/dev-test-server.mjs
2026-04-24 12:42:12 +08:00

97 lines
3.1 KiB
JavaScript

import { createServer } from "node:http";
import { createReadStream, statSync } from "node:fs";
import { extname, join, normalize } from "node:path";
import { fileURLToPath } from "node:url";
const rootDir = fileURLToPath(new URL(".", import.meta.url));
const port = Number(process.env.INVITE_TEST_PORT || 65248);
const apiTarget = String(process.env.INVITE_API_TARGET || "http://127.0.0.1:2900").replace(/\/$/, "");
const mimeTypes = {
".html": "text/html; charset=utf-8",
".js": "text/javascript; charset=utf-8",
".css": "text/css; charset=utf-8",
".json": "application/json; charset=utf-8",
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".svg": "image/svg+xml",
};
function sendText(response, status, text) {
response.writeHead(status, { "content-type": "text/plain; charset=utf-8" });
response.end(text);
}
function serveStatic(request, response) {
const requestPath = new URL(request.url, "http://localhost").pathname;
const pathname = requestPath === "/" ? "/dev-test.html" : requestPath;
const filePath = normalize(join(rootDir, pathname));
if (!filePath.startsWith(rootDir)) {
sendText(response, 403, "Forbidden");
return;
}
try {
const stat = statSync(filePath);
if (!stat.isFile()) {
sendText(response, 404, "Not found");
return;
}
response.writeHead(200, {
"content-type": mimeTypes[extname(filePath)] || "application/octet-stream",
"content-length": stat.size,
});
createReadStream(filePath).pipe(response);
} catch {
sendText(response, 404, "Not found");
}
}
async function proxyAPI(request, response) {
const incomingURL = new URL(request.url, "http://localhost");
const targetURL = new URL(incomingURL.pathname.replace(/^\/api/, "") + incomingURL.search, apiTarget);
const headers = new Headers();
for (const [key, value] of Object.entries(request.headers)) {
if (value !== undefined && key.toLowerCase() !== "host") {
headers.set(key, Array.isArray(value) ? value.join(",") : value);
}
}
try {
const upstream = await fetch(targetURL, {
method: request.method,
headers,
body: request.method === "GET" || request.method === "HEAD" ? undefined : request,
duplex: "half",
});
response.writeHead(upstream.status, {
"content-type": upstream.headers.get("content-type") || "application/octet-stream",
});
if (upstream.body) {
for await (const chunk of upstream.body) {
response.write(chunk);
}
}
response.end();
} catch (error) {
response.writeHead(502, { "content-type": "application/json; charset=utf-8" });
response.end(JSON.stringify({
status: false,
code: "proxy_error",
message: error instanceof Error ? error.message : "proxy error",
}));
}
}
createServer((request, response) => {
if (request.url?.startsWith("/api/") || request.url === "/api") {
proxyAPI(request, response);
return;
}
serveStatic(request, response);
}).listen(port, "127.0.0.1", () => {
console.log(`Invite dev test page: http://127.0.0.1:${port}/dev-test.html`);
console.log(`Proxy target: ${apiTarget}`);
});