feat(infra): add Docker and nginx infrastructure for frontend

Dev: Vite dev server in Node 24 container with hot reload via volume
mount and named volume for node_modules isolation.

Prod: Multi-stage build (pnpm build → nginx) serving static files with
API/WebSocket reverse proxy to backend.
This commit is contained in:
CarterPerez-dev 2026-02-28 11:21:11 -05:00
parent 17efa613bf
commit 012d849366
7 changed files with 150 additions and 1 deletions

View File

@ -13,6 +13,7 @@ API_KEY=changeme-generate-a-real-key
POSTGRES_HOST_PORT=16969
REDIS_HOST_PORT=26969
BACKEND_HOST_PORT=36969
FRONTEND_HOST_PORT=46969
# PostgreSQL
POSTGRES_DB=angelusvigil

View File

@ -74,6 +74,27 @@ services:
start_period: 10s
restart: always
frontend:
build:
context: .
dockerfile: infra/docker/vite.prod
container_name: vigil-frontend
ports:
- "${FRONTEND_HOST_PORT:-80}:80"
depends_on:
backend:
condition: service_healthy
networks:
- vigil_network
- certgames_net
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
restart: always
geoip-updater:
image: maxmindinc/geoipupdate:latest
container_name: vigil-geoip

View File

@ -71,6 +71,24 @@ services:
retries: 3
start_period: 15s
frontend:
build:
context: .
dockerfile: infra/docker/vite.dev
container_name: vigil-frontend-dev
environment:
VITE_API_TARGET: http://backend:8000
ports:
- "${FRONTEND_HOST_PORT:-46969}:5173"
volumes:
- ./frontend:/app
- frontend_node_modules_dev:/app/node_modules
depends_on:
backend:
condition: service_healthy
networks:
- vigil_dev
networks:
vigil_dev:
driver: bridge
@ -79,3 +97,4 @@ volumes:
postgres_dev:
redis_dev:
nginx_logs_dev:
frontend_node_modules_dev:

View File

@ -11,6 +11,10 @@ import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, path.resolve(__dirname, '..'), '')
const isDev = mode === 'development'
const apiTarget =
process.env.VITE_API_TARGET ||
env.VITE_API_TARGET ||
'http://localhost:8000'
return {
plugins: [react(), tsconfigPaths()],
@ -32,10 +36,15 @@ export default defineConfig(({ mode }) => {
host: '0.0.0.0',
proxy: {
'/api': {
target: env.VITE_API_TARGET || 'http://localhost:8000',
target: apiTarget,
changeOrigin: true,
rewrite: (p) => p.replace(/^\/api/, ''),
},
'/ws': {
target: apiTarget,
ws: true,
changeOrigin: true,
},
},
},

View File

@ -0,0 +1,18 @@
# ©AngelaMos | 2026
# Development Vite Dockerfile
# Hot reload with volume mounts for source code
FROM node:24-alpine
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
COPY frontend/package.json ./
RUN pnpm install
COPY frontend/ .
EXPOSE 5173
CMD ["pnpm", "dev"]

View File

@ -0,0 +1,29 @@
# ©AngelaMos | 2026
# Production Vite Dockerfile
# Multi-stage build: pnpm build → nginx static serving
FROM node:24-alpine AS builder
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
COPY frontend/package.json ./
RUN pnpm install --frozen-lockfile
COPY frontend/ .
RUN pnpm build
FROM nginx:alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY infra/nginx/vigil.conf /etc/nginx/conf.d/vigil.conf
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost/health || exit 1
CMD ["nginx", "-g", "daemon off;"]

View File

@ -0,0 +1,52 @@
# ©AngelaMos | 2026
# vigil.conf
upstream vigil_backend {
server backend:8000;
}
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 256;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://vigil_backend/;
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;
proxy_read_timeout 30s;
}
location /ws/ {
proxy_pass http://vigil_backend/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 86400s;
}
location /health {
access_log off;
return 200 'ok';
add_header Content-Type text/plain;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}