From 175d6f0a7bb55f03df70f3212f0497b7a6a53adc Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Sat, 14 Feb 2026 21:42:15 -0500 Subject: [PATCH] =?UTF-8?q?scaffold:=20Docker=20infrastructure=20=E2=80=94?= =?UTF-8?q?=20Compose,=20Nginx,=20NATS,=20Dockerfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4-service stack (PostgreSQL 16, NATS JetStream, Rust API, Nginx), multi-stage Rust build with dependency caching, wildcard subdomain routing for public sites, WebSocket support, rate limiting zones. Co-Authored-By: Claude Opus 4.6 --- docker/Dockerfile.api | 33 ++++++++ docker/docker-compose.yml | 89 ++++++++++++++++++++++ docker/nats.conf | 35 +++++++++ docker/nginx.conf | 153 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 310 insertions(+) create mode 100644 docker/Dockerfile.api create mode 100644 docker/docker-compose.yml create mode 100644 docker/nats.conf create mode 100644 docker/nginx.conf diff --git a/docker/Dockerfile.api b/docker/Dockerfile.api new file mode 100644 index 0000000..5b40ca6 --- /dev/null +++ b/docker/Dockerfile.api @@ -0,0 +1,33 @@ +# Multi-stage build for Corrosion API +# Stage 1: Build the Rust binary +FROM rust:1.84-alpine AS builder + +RUN apk add --no-cache musl-dev pkgconfig openssl-dev openssl-libs-static + +WORKDIR /build + +# Cache dependencies — copy manifests first +COPY Cargo.toml Cargo.lock ./ +RUN mkdir src && echo "fn main() {}" > src/main.rs +RUN cargo build --release 2>/dev/null || true + +# Now copy actual source and build +COPY src/ src/ +COPY migrations/ migrations/ +RUN touch src/main.rs && cargo build --release + +# Stage 2: Runtime image +FROM alpine:3.20 + +RUN apk add --no-cache ca-certificates + +WORKDIR /app + +COPY --from=builder /build/target/release/corrosion-api . +COPY migrations/ migrations/ + +ENV RUST_LOG=corrosion_api=info,tower_http=info + +EXPOSE 3000 + +CMD ["./corrosion-api"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..7697480 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,89 @@ +version: '3.8' + +services: + postgres: + image: postgres:16-alpine + container_name: corrosion-db + environment: + POSTGRES_DB: corrosion + POSTGRES_USER: corrosion + POSTGRES_PASSWORD: ${DB_PASSWORD:-corrosion_dev} + volumes: + - pg_data:/var/lib/postgresql/data + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U corrosion"] + interval: 5s + timeout: 5s + retries: 5 + + nats: + image: nats:latest + container_name: corrosion-nats + command: + - "--jetstream" + - "--store_dir=/data" + - "--config=/etc/nats/nats.conf" + volumes: + - nats_data:/data + - ./nats.conf:/etc/nats/nats.conf:ro + ports: + - "4222:4222" # Client connections + - "8222:8222" # Monitoring + - "9222:9222" # WebSocket (frontend real-time) + healthcheck: + test: ["CMD", "nats-server", "--signal", "ldm"] + interval: 10s + timeout: 5s + retries: 3 + + api: + build: + context: ../backend + dockerfile: ../docker/Dockerfile.api + container_name: corrosion-api + environment: + DATABASE_URL: postgres://corrosion:${DB_PASSWORD:-corrosion_dev}@postgres:5432/corrosion + NATS_URL: nats://nats:4222 + JWT_SECRET: ${JWT_SECRET} + ENCRYPTION_KEY: ${ENCRYPTION_KEY} + CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN} + CLOUDFLARE_ZONE_ID: ${CLOUDFLARE_ZONE_ID} + STEAM_API_KEY: ${STEAM_API_KEY} + SMTP_HOST: ${SMTP_HOST:-localhost} + SMTP_PORT: ${SMTP_PORT:-587} + SMTP_USERNAME: ${SMTP_USERNAME} + SMTP_PASSWORD: ${SMTP_PASSWORD} + SMTP_FROM: ${SMTP_FROM:-noreply@corrosionmgmt.com} + FRONTEND_URL: ${FRONTEND_URL:-https://panel.corrosionmgmt.com} + RUST_LOG: corrosion_api=info,tower_http=info + volumes: + - map_data:/data/maps + - backup_data:/data/backups + depends_on: + postgres: + condition: service_healthy + nats: + condition: service_healthy + ports: + - "3000:3000" + + nginx: + image: nginx:alpine + container_name: corrosion-nginx + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + - ../frontend/dist:/usr/share/nginx/html:ro + - map_data:/data/maps:ro + depends_on: + - api + +volumes: + pg_data: + nats_data: + map_data: + backup_data: diff --git a/docker/nats.conf b/docker/nats.conf new file mode 100644 index 0000000..f2691a1 --- /dev/null +++ b/docker/nats.conf @@ -0,0 +1,35 @@ +# Corrosion NATS Configuration +# JetStream enabled for persistent messaging + +listen: 0.0.0.0:4222 + +# JetStream configuration +jetstream { + store_dir: /data + max_mem: 256MB + max_file: 2GB +} + +# WebSocket listener for frontend real-time updates +websocket { + listen: "0.0.0.0:9222" + no_tls: true # TLS terminated at Nginx/Cloudflare +} + +# HTTP monitoring +http: 0.0.0.0:8222 + +# Logging +debug: false +trace: false +logtime: true + +# Limits +max_payload: 8MB # Support map file transfer metadata +max_connections: 10000 + +# Authorization — tokens validated per-connection +# Plugin and companion agents authenticate with license-specific tokens +authorization { + timeout: 5 +} diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 0000000..6c6f19d --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,153 @@ +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + client_max_body_size 250M; # Map uploads up to 200MB + overhead + + # Gzip + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml; + + # Logging + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + # Rate limiting zones + limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/s; + limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s; + + # Upstream API + upstream api { + server api:3000; + } + + # Main server — panel.corrosionmgmt.com + server { + listen 80; + server_name panel.corrosionmgmt.com; + + # API proxy + location /api/ { + limit_req zone=api burst=50 nodelay; + proxy_pass http://api; + 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; + + # WebSocket support for console streaming + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + # Auth endpoints — stricter rate limiting + location /api/auth/ { + limit_req zone=auth burst=10 nodelay; + proxy_pass http://api; + 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; + } + + # Frontend SPA + location / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + } + } + + # Wildcard server — *.corrosionmgmt.com (public server sites) + server { + listen 80; + server_name *.corrosionmgmt.com; + + # Public API proxy (subset of endpoints) + location /api/public/ { + limit_req zone=api burst=50 nodelay; + proxy_pass http://api; + 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; + } + + # Store API proxy + location /api/store/ { + limit_req zone=api burst=20 nodelay; + proxy_pass http://api; + 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; + } + + # PayPal webhook + location /api/store/webhook/ { + proxy_pass http://api; + 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; + } + + # Public site frontend + location / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + } + } + + # Status page — status.corrosionmgmt.com + server { + listen 80; + server_name status.corrosionmgmt.com; + + location /api/public/ { + proxy_pass http://api; + 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 / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + } + } + + # Map file downloads — direct file serving with sendfile + server { + listen 80; + server_name api.corrosionmgmt.com; + + location /maps/ { + alias /data/maps/; + sendfile on; + tcp_nopush on; + aio on; + } + + location / { + proxy_pass http://api; + 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_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + } +}