Serve the frontend from docker-compose via nginx
Milestone 9 originally planned Caddy as the reverse proxy handling both static serving and TLS. That's changed: the user has their own external nginx that will handle SSL/certs and route to this app once it's exposed beyond the LAN — so this compose stack only needs to serve the frontend over plain HTTP for now, no TLS layer of its own. Rewrote frontend/Dockerfile from the old scratch/dist-only build (meant to hand its output to Caddy) into a self-contained nginx image: builds the Vite app, then serves it from nginx on port 80. Added frontend/nginx.conf, which also reverse-proxies /api, /auth, /users, /ws, and /health to the backend container — necessary because api/client.ts calls relative paths, so the SPA and API need to appear as one origin to the browser (same pattern the Vite dev-server proxy already used, now the production equivalent). The /ws location sets the Upgrade/Connection headers for the WebSocket handshake and a long proxy_read_timeout, since DM turns can take well over nginx's 60s default before the connection sees more traffic. docker-compose.yml gets a frontend service, port 8080:80. Verified: docker compose up -d --build frontend; confirmed the built SPA loads, /api and /health proxy through to the backend (401/200, not 502), and — the part most likely to break — a real WebSocket opened successfully through the proxy and a full message round-trip (send -> persist -> broadcast -> typing -> DM turn) worked end to end against the containerized stack, not just the Vite dev server. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,5 +27,13 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build: ./frontend
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.git
|
||||||
+6
-4
@@ -1,9 +1,11 @@
|
|||||||
FROM node:22-slim AS build
|
FROM node:22-slim AS build
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package.json .
|
COPY package.json package-lock.json .
|
||||||
RUN npm install
|
RUN npm ci
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM scratch AS dist
|
FROM nginx:1.27-alpine
|
||||||
COPY --from=build /app/dist /
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
EXPOSE 80
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Same-origin as far as the browser is concerned — api/client.ts calls relative paths, so
|
||||||
|
# this proxies them to the backend container internally instead of relying on CORS.
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://backend:8000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /auth/ {
|
||||||
|
proxy_pass http://backend:8000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /users/ {
|
||||||
|
proxy_pass http://backend:8000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /health {
|
||||||
|
proxy_pass http://backend:8000;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /ws/ {
|
||||||
|
proxy_pass http://backend:8000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# SPA fallback — client-side routes (e.g. /games/<id>) must resolve to index.html.
|
||||||
|
location / {
|
||||||
|
try_files $uri /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user