Spaces:
Running
Running
fix server.js
Browse files- Dockerfile +8 -26
- server.js +85 -0
Dockerfile
CHANGED
|
@@ -12,31 +12,13 @@ COPY . .
|
|
| 12 |
RUN pnpm build
|
| 13 |
|
| 14 |
# Stage 2: Serve
|
| 15 |
-
FROM
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
listen 7860;\n\
|
| 22 |
-
root /usr/share/nginx/html;\n\
|
| 23 |
-
index index.html;\n\
|
| 24 |
-
\n\
|
| 25 |
-
location /api/ {\n\
|
| 26 |
-
resolver 8.8.8.8 valid=30s;\n\
|
| 27 |
-
set $backend ${VITE_API_BASE_URL};\n\
|
| 28 |
-
proxy_pass $backend/api/;\n\
|
| 29 |
-
proxy_set_header Host $proxy_host;\n\
|
| 30 |
-
proxy_set_header X-Real-IP $remote_addr;\n\
|
| 31 |
-
proxy_ssl_server_name on;\n\
|
| 32 |
-
}\n\
|
| 33 |
-
\n\
|
| 34 |
-
location / {\n\
|
| 35 |
-
try_files $uri $uri/ /index.html;\n\
|
| 36 |
-
}\n\
|
| 37 |
-
}\n' > /etc/nginx/conf.d/default.conf.template
|
| 38 |
-
|
| 39 |
-
# Entrypoint: substitute env vars into nginx config, then start nginx
|
| 40 |
-
CMD ["/bin/sh", "-c", "envsubst '$VITE_API_BASE_URL' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"]
|
| 41 |
|
| 42 |
EXPOSE 7860
|
|
|
|
|
|
|
|
|
| 12 |
RUN pnpm build
|
| 13 |
|
| 14 |
# Stage 2: Serve
|
| 15 |
+
FROM node:20-alpine
|
| 16 |
+
|
| 17 |
+
WORKDIR /app
|
| 18 |
+
|
| 19 |
+
COPY --from=builder /app/dist ./dist
|
| 20 |
+
COPY server.js .
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
EXPOSE 7860
|
| 23 |
+
|
| 24 |
+
CMD ["node", "server.js"]
|
server.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const http = require("http");
|
| 2 |
+
const https = require("https");
|
| 3 |
+
const fs = require("fs");
|
| 4 |
+
const path = require("path");
|
| 5 |
+
const url = require("url");
|
| 6 |
+
|
| 7 |
+
const PORT = 7860;
|
| 8 |
+
const DIST_DIR = path.join(__dirname, "dist");
|
| 9 |
+
const BACKEND_URL = process.env.VITE_API_BASE_URL || "";
|
| 10 |
+
|
| 11 |
+
const MIME = {
|
| 12 |
+
".html": "text/html",
|
| 13 |
+
".js": "application/javascript",
|
| 14 |
+
".mjs": "application/javascript",
|
| 15 |
+
".css": "text/css",
|
| 16 |
+
".json": "application/json",
|
| 17 |
+
".png": "image/png",
|
| 18 |
+
".jpg": "image/jpeg",
|
| 19 |
+
".jpeg": "image/jpeg",
|
| 20 |
+
".svg": "image/svg+xml",
|
| 21 |
+
".ico": "image/x-icon",
|
| 22 |
+
".woff": "font/woff",
|
| 23 |
+
".woff2": "font/woff2",
|
| 24 |
+
".ttf": "font/ttf",
|
| 25 |
+
".webp": "image/webp",
|
| 26 |
+
};
|
| 27 |
+
|
| 28 |
+
console.log(`Starting server on port ${PORT}`);
|
| 29 |
+
console.log(`Backend URL: ${BACKEND_URL || "(not set)"}`);
|
| 30 |
+
|
| 31 |
+
const server = http.createServer((req, res) => {
|
| 32 |
+
const parsed = url.parse(req.url);
|
| 33 |
+
|
| 34 |
+
// Proxy /api/* to backend
|
| 35 |
+
if (parsed.pathname.startsWith("/api/") && BACKEND_URL) {
|
| 36 |
+
const backend = url.parse(BACKEND_URL);
|
| 37 |
+
const isHttps = backend.protocol === "https:";
|
| 38 |
+
const transport = isHttps ? https : http;
|
| 39 |
+
|
| 40 |
+
const options = {
|
| 41 |
+
hostname: backend.hostname,
|
| 42 |
+
port: backend.port || (isHttps ? 443 : 80),
|
| 43 |
+
path: req.url,
|
| 44 |
+
method: req.method,
|
| 45 |
+
headers: { ...req.headers, host: backend.hostname },
|
| 46 |
+
};
|
| 47 |
+
|
| 48 |
+
const proxyReq = transport.request(options, (proxyRes) => {
|
| 49 |
+
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
| 50 |
+
proxyRes.pipe(res);
|
| 51 |
+
});
|
| 52 |
+
|
| 53 |
+
proxyReq.on("error", (err) => {
|
| 54 |
+
console.error("Proxy error:", err.message);
|
| 55 |
+
res.writeHead(502);
|
| 56 |
+
res.end("Bad Gateway");
|
| 57 |
+
});
|
| 58 |
+
|
| 59 |
+
req.pipe(proxyReq);
|
| 60 |
+
return;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
// Serve static files
|
| 64 |
+
let filePath = path.join(DIST_DIR, parsed.pathname);
|
| 65 |
+
try {
|
| 66 |
+
const stat = fs.statSync(filePath);
|
| 67 |
+
if (stat.isDirectory()) filePath = path.join(filePath, "index.html");
|
| 68 |
+
} catch {
|
| 69 |
+
// Not found → SPA fallback
|
| 70 |
+
filePath = path.join(DIST_DIR, "index.html");
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
fs.readFile(filePath, (err, data) => {
|
| 74 |
+
if (err) {
|
| 75 |
+
res.writeHead(404);
|
| 76 |
+
res.end("Not Found");
|
| 77 |
+
return;
|
| 78 |
+
}
|
| 79 |
+
const ext = path.extname(filePath).toLowerCase();
|
| 80 |
+
res.writeHead(200, { "Content-Type": MIME[ext] || "application/octet-stream" });
|
| 81 |
+
res.end(data);
|
| 82 |
+
});
|
| 83 |
+
});
|
| 84 |
+
|
| 85 |
+
server.listen(PORT);
|