feat(docker): add nginx frontend image and full-stack compose

Build static assets with Vite, proxy /api to backend, and document server env vars.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-06 10:13:43 +02:00
co-authored by Cursor
parent b9942b49b8
commit 653f1b0c5a
5 changed files with 134 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
node_modules
dist
dist-ssr
coverage
.git
.env
.env.docker
.env.local
*.log
.vite
*.tsbuildinfo
+12
View File
@@ -0,0 +1,12 @@
# Copy to .env.docker on the server (never commit secrets).
#
# Build-time: API base URL for the Vue app (/api = nginx proxies to backend).
VITE_API_URL=/api
# Host port for the frontend container
FRONTEND_PORT=8080
# Backend env (used by ../backend-nest service in compose)
JWT_SECRET=replace-with-a-long-random-secret
# Must match the public URL users open in the browser (scheme + host + port).
CORS_ORIGIN=http://your-server:8080
+26
View File
@@ -0,0 +1,26 @@
# syntax=docker/dockerfile:1
FROM node:22.12.0-bookworm-slim AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
# Baked in at build time. Default keeps browser requests on /api (nginx proxy).
ARG VITE_API_URL=/api
ENV VITE_API_URL=${VITE_API_URL}
RUN npm run build
FROM nginx:1.27-alpine AS production
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
+58
View File
@@ -0,0 +1,58 @@
services:
backend:
build:
context: ../backend-nest
dockerfile: Dockerfile
image: child-gold-land-backend:latest
container_name: child-gold-land-backend
env_file:
- .env.docker
environment:
NODE_ENV: production
PORT: 3000
HOST: 0.0.0.0
DB_PATH: /app/data/app.db
JWT_SECRET: ${JWT_SECRET}
CORS_ORIGIN: ${CORS_ORIGIN}
volumes:
- backend_sqlite:/app/data
restart: unless-stopped
healthcheck:
test:
[
"CMD",
"node",
"-e",
"fetch('http://127.0.0.1:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))",
]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
networks:
- child-gold-land
frontend:
build:
context: .
dockerfile: Dockerfile
args:
VITE_API_URL: ${VITE_API_URL:-/api}
image: child-gold-land-frontend:latest
container_name: child-gold-land-frontend
ports:
- "${FRONTEND_PORT:-8080}:80"
depends_on:
backend:
condition: service_healthy
restart: unless-stopped
networks:
- child-gold-land
volumes:
backend_sqlite:
name: child-gold-land-sqlite
networks:
child-gold-land:
name: child-gold-land
+27
View File
@@ -0,0 +1,27 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# API requests proxied to the NestJS backend (same Docker network).
location /api/ {
proxy_pass http://backend:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /health {
proxy_pass http://backend:3000/health;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
location / {
try_files $uri $uri/ /index.html;
}
}