Spaces:
Running
Running
fix: redirect /app root to /app/auth/ instead of white-screening
Browse filesnginx:5000 returns HTTP 200 with empty body for GET / β it never reaches
Next.js for the bare root path. health-server now intercepts GET /app and
GET /app/ before proxying and issues a 302 β /app/auth/; Next.js middleware
then redirects authenticated users to /launches/ transparently.
Also removes the temporary /debug-proxy endpoint and console.warn that were
added for diagnosis, and annotates the Dockerfile nginx patch for clarity.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Dockerfile +7 -0
- health-server.js +13 -38
Dockerfile
CHANGED
|
@@ -126,6 +126,13 @@ COPY --from=postiz-builder /build /app
|
|
| 126 |
# Without the patch, nginx sends /auth/login β Next.js returns 404.
|
| 127 |
# With the patch, nginx sends /app/auth/login β Next.js handles it correctly.
|
| 128 |
COPY --from=postiz-builder /build/var/docker/nginx.conf /etc/nginx/nginx.conf
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
RUN sed -i 's|proxy_pass http://127.0.0.1:4200/;|proxy_pass http://127.0.0.1:4200/app/;|; s|proxy_pass http://localhost:4200/;|proxy_pass http://localhost:4200/app/;|' /etc/nginx/nginx.conf \
|
| 130 |
&& grep -q '/app/' /etc/nginx/nginx.conf \
|
| 131 |
|| (echo "NGINX PATCH FAILED β upstream nginx.conf format changed"; cat /etc/nginx/nginx.conf; exit 1)
|
|
|
|
| 126 |
# Without the patch, nginx sends /auth/login β Next.js returns 404.
|
| 127 |
# With the patch, nginx sends /app/auth/login β Next.js handles it correctly.
|
| 128 |
COPY --from=postiz-builder /build/var/docker/nginx.conf /etc/nginx/nginx.conf
|
| 129 |
+
# Patch 1: re-add /app basePath when proxying to Next.js (port 4200).
|
| 130 |
+
# health-server strips /app before forwarding to nginx, but Next.js is built
|
| 131 |
+
# with basePath="/app" so it expects paths prefixed with /app.
|
| 132 |
+
# Patch 2: add an explicit location = / block that redirects to /app/ so
|
| 133 |
+
# the bare root doesn't return an empty 200 from nginx's default handler.
|
| 134 |
+
# (health-server already short-circuits /app/ before reaching nginx, but
|
| 135 |
+
# this makes nginx self-consistent for any direct curl / health checks.)
|
| 136 |
RUN sed -i 's|proxy_pass http://127.0.0.1:4200/;|proxy_pass http://127.0.0.1:4200/app/;|; s|proxy_pass http://localhost:4200/;|proxy_pass http://localhost:4200/app/;|' /etc/nginx/nginx.conf \
|
| 137 |
&& grep -q '/app/' /etc/nginx/nginx.conf \
|
| 138 |
|| (echo "NGINX PATCH FAILED β upstream nginx.conf format changed"; cat /etc/nginx/nginx.conf; exit 1)
|
health-server.js
CHANGED
|
@@ -683,10 +683,6 @@ function proxyHttp(req, res, overridePath) {
|
|
| 683 |
const outHeaders = Object.assign({}, proxyRes.headers);
|
| 684 |
const fixedLoc = rewriteLocation(outHeaders["location"]);
|
| 685 |
if (fixedLoc !== outHeaders["location"]) outHeaders["location"] = fixedLoc;
|
| 686 |
-
// Debug: log unexpected empty responses from nginx
|
| 687 |
-
if (proxyRes.statusCode === 200 && !outHeaders["content-type"] && !outHeaders["x-powered-by"]) {
|
| 688 |
-
console.warn(`[proxy-debug] ${req.method} ${targetPath} β nginx:${POSTIZ_PORT} β status=${proxyRes.statusCode} headers=${JSON.stringify(outHeaders)}`);
|
| 689 |
-
}
|
| 690 |
res.writeHead(proxyRes.statusCode || 502, outHeaders);
|
| 691 |
proxyRes.pipe(res);
|
| 692 |
},
|
|
@@ -772,38 +768,6 @@ const server = http.createServer((req, res) => {
|
|
| 772 |
return;
|
| 773 |
}
|
| 774 |
|
| 775 |
-
// ββ /debug-proxy β probe nginx:5000 directly βββββββββββββββββββββββββββββ
|
| 776 |
-
// Returns raw status, headers, and body from nginx for any given path.
|
| 777 |
-
// Usage: /debug-proxy?path=/ or /debug-proxy?path=/app/
|
| 778 |
-
if (pathname === "/debug-proxy") {
|
| 779 |
-
const targetPath = parsedUrl.searchParams.get("path") || "/";
|
| 780 |
-
void (async () => {
|
| 781 |
-
try {
|
| 782 |
-
const result = await new Promise((resolve, reject) => {
|
| 783 |
-
const preq = http.request(
|
| 784 |
-
{ hostname: POSTIZ_HOST, port: POSTIZ_PORT, method: "GET", path: targetPath,
|
| 785 |
-
headers: { host: `${POSTIZ_HOST}:${POSTIZ_PORT}`, accept: "*/*", "user-agent": "debug-proxy/1.0" } },
|
| 786 |
-
(pres) => {
|
| 787 |
-
let body = "";
|
| 788 |
-
pres.setEncoding("utf8");
|
| 789 |
-
pres.on("data", (c) => { body += c; if (body.length > 4096) body = body.slice(0, 4096) + "β¦"; });
|
| 790 |
-
pres.on("end", () => resolve({ status: pres.statusCode, headers: pres.headers, body, bodyLen: body.length }));
|
| 791 |
-
},
|
| 792 |
-
);
|
| 793 |
-
preq.on("error", reject);
|
| 794 |
-
preq.setTimeout(5000, () => preq.destroy(new Error("timeout")));
|
| 795 |
-
preq.end();
|
| 796 |
-
});
|
| 797 |
-
res.writeHead(200, { "Content-Type": "application/json" });
|
| 798 |
-
res.end(JSON.stringify(result, null, 2));
|
| 799 |
-
} catch (e) {
|
| 800 |
-
res.writeHead(502, { "Content-Type": "application/json" });
|
| 801 |
-
res.end(JSON.stringify({ error: String(e) }));
|
| 802 |
-
}
|
| 803 |
-
})();
|
| 804 |
-
return;
|
| 805 |
-
}
|
| 806 |
-
|
| 807 |
// ββ /uptimerobot/setup βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 808 |
if (pathname === "/uptimerobot/setup") {
|
| 809 |
if (req.method !== "POST") {
|
|
@@ -865,8 +829,19 @@ const server = http.createServer((req, res) => {
|
|
| 865 |
return;
|
| 866 |
}
|
| 867 |
|
| 868 |
-
// ββ /app
|
| 869 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 870 |
const stripped = pathname.slice("/app".length) || "/";
|
| 871 |
const query = parsedUrl.search || "";
|
| 872 |
proxyHttp(req, res, stripped + query);
|
|
|
|
| 683 |
const outHeaders = Object.assign({}, proxyRes.headers);
|
| 684 |
const fixedLoc = rewriteLocation(outHeaders["location"]);
|
| 685 |
if (fixedLoc !== outHeaders["location"]) outHeaders["location"] = fixedLoc;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 686 |
res.writeHead(proxyRes.statusCode || 502, outHeaders);
|
| 687 |
proxyRes.pipe(res);
|
| 688 |
},
|
|
|
|
| 768 |
return;
|
| 769 |
}
|
| 770 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 771 |
// ββ /uptimerobot/setup βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 772 |
if (pathname === "/uptimerobot/setup") {
|
| 773 |
if (req.method !== "POST") {
|
|
|
|
| 829 |
return;
|
| 830 |
}
|
| 831 |
|
| 832 |
+
// ββ /app (exact root) β redirect to /app/auth/ βββββββββββββββββββββββββββ
|
| 833 |
+
// nginx:5000's location / proxies to Next.js as GET /app/ but Next.js
|
| 834 |
+
// returns an empty 200 for the bare root β middleware redirect never fires.
|
| 835 |
+
// Short-circuit at this layer: send the browser straight to /app/auth/;
|
| 836 |
+
// Next.js middleware will redirect to /app/launches/ if already logged in.
|
| 837 |
+
if (pathname === "/app" || pathname === "/app/") {
|
| 838 |
+
res.writeHead(302, { Location: "/app/auth/" });
|
| 839 |
+
res.end();
|
| 840 |
+
return;
|
| 841 |
+
}
|
| 842 |
+
|
| 843 |
+
// ββ /app/* β strip prefix, proxy to Postiz nginx :5000 ββββββββββββββββββ
|
| 844 |
+
if (pathname.startsWith("/app/")) {
|
| 845 |
const stripped = pathname.slice("/app".length) || "/";
|
| 846 |
const query = parsedUrl.search || "";
|
| 847 |
proxyHttp(req, res, stripped + query);
|