-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserve.js
More file actions
85 lines (71 loc) · 3.04 KB
/
Copy pathserve.js
File metadata and controls
85 lines (71 loc) · 3.04 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
/**
* Production server for userhub-shell.
*
* Serves only the pre-built dist/ folder — no source code, no Vite.
*
* Routing:
* /api/* → forwarded to API_URL (full path preserved, e.g. /api/auth/login)
* /remote/* → forwarded to REMOTE_URL (/remote prefix stripped)
* everything else → dist/index.html (SPA fallback for React Router)
*
* Uses Node's built-in http module — no extra proxy dependencies needed.
*/
import express from 'express';
import http from 'http';
import https from 'https';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const app = express();
const PORT = process.env.PORT || 4173;
const API_URL = process.env.API_URL || 'http://localhost:3001';
const REMOTE_URL = process.env.REMOTE_URL || 'http://localhost:5001';
/**
* Forward `req` to `targetBase` using `req.url` as the path.
* Mounted at the root level so Express does NOT strip any prefix —
* /api/auth/login arrives at the API as /api/auth/login (not /auth/login).
*/
function forward(req, res, targetBase) {
const target = new URL(targetBase);
const client = target.protocol === 'https:' ? https : http;
const options = {
hostname: target.hostname,
port: target.port || (target.protocol === 'https:' ? 443 : 80),
path: req.url, // full path, e.g. /api/auth/register
method: req.method,
headers: { ...req.headers, host: target.host },
};
const proxyReq = client.request(options, (proxyRes) => {
res.writeHead(proxyRes.statusCode, proxyRes.headers);
proxyRes.pipe(res, { end: true });
});
proxyReq.on('error', (err) => {
console.error(`[Proxy error] ${req.method} ${req.url} → ${targetBase} : ${err.message}`);
if (!res.headersSent) res.status(502).send('Bad Gateway — upstream not reachable');
});
req.pipe(proxyReq, { end: true });
}
// ── Root-level proxy middleware (runs before static/SPA handlers) ─────────────
app.use((req, res, next) => {
// /api/* → API server — full path forwarded as-is
if (req.url.startsWith('/api')) {
return forward(req, res, API_URL);
}
// /remote/* → remote-app — strip the /remote prefix
if (req.url.startsWith('/remote')) {
req.url = req.url.slice('/remote'.length) || '/';
return forward(req, res, REMOTE_URL);
}
next();
});
// ── Serve built assets from dist/ ────────────────────────────────────────────
app.use(express.static(path.join(__dirname, 'dist')));
// ── SPA fallback — React Router handles client-side routing ──────────────────
app.get('*', (_req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`✅ Shell serving dist/ on http://localhost:${PORT}`);
console.log(` API proxy → ${API_URL}`);
console.log(` Remote proxy → ${REMOTE_URL}`);
});