- React + MUI DataGrid app with faction filter, search, change filter - Biggest movers cards (drops/rises) scoped to current filter view - Historical points graph modal (5 MFM versions: 1.14 → current) - URL state sync (faction, dir, q params — shareable URLs) - Grimdark favicon + OG embed image (Google Imagen) - Multi-stage Dockerfile (node build → nginx serve) - docker-compose.yml with Traefik + Cloudflare TLS - Data pipeline: build_deduped_data.py merges PDF + live scrape - Ynnari merged into Aeldari (shared codex) - Mobile responsive: flex columns, no fixed pixel widths - Color semantics: green=cheaper, red=costlier (consistent everywhere) - 1,449 units across 31 factions
43 lines
1.1 KiB
Docker
43 lines
1.1 KiB
Docker
# ── Build stage: React app ──
|
|
FROM node:20-alpine AS build
|
|
|
|
WORKDIR /app
|
|
COPY react-app/package.json react-app/package-lock.json* ./
|
|
RUN npm ci --silent || npm install --silent
|
|
|
|
COPY react-app/ ./
|
|
RUN npm run build
|
|
|
|
# ── Runtime stage: nginx ──
|
|
FROM nginx:alpine
|
|
|
|
# Copy built assets from build stage
|
|
COPY --from=build /app/dist/ /usr/share/nginx/html/
|
|
|
|
# SPA-friendly nginx config with CORS headers for module scripts
|
|
RUN printf 'server {\n\
|
|
listen 80;\n\
|
|
server_name _;\n\
|
|
root /usr/share/nginx/html;\n\
|
|
index index.html;\n\
|
|
\n\
|
|
gzip on;\n\
|
|
gzip_types text/plain text/css application/javascript application/json image/svg+xml;\n\
|
|
gzip_min_length 256;\n\
|
|
\n\
|
|
# CORS + cache for static assets\n\
|
|
location ~* \.(js|css|json|png|jpg|webp|svg|ico|woff2?)$ {\n\
|
|
expires 1h;\n\
|
|
add_header Cache-Control "public, max-age=3600";\n\
|
|
add_header Access-Control-Allow-Origin "*" always;\n\
|
|
}\n\
|
|
\n\
|
|
location / {\n\
|
|
add_header Access-Control-Allow-Origin "*" always;\n\
|
|
try_files $uri $uri/ /index.html;\n\
|
|
}\n\
|
|
}\n' > /etc/nginx/conf.d/default.conf
|
|
|
|
EXPOSE 80
|
|
|
|
CMD ["nginx", "-g", "daemon off;"] |