diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..9ba153ef --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM node:22-alpine AS builder +WORKDIR /app +RUN apk add --no-cache git +COPY package*.json ./ +COPY .npmrc ./ +COPY scripts/ ./scripts/ +RUN npm ci +COPY . . +RUN npm run build + +FROM nginx:alpine +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;"] diff --git a/README.md b/README.md index d28bcbe3..c8c40a5d 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,36 @@ npm run dev Development server: `http://localhost:8080` +### Docker Getting Started + +Use Docker Compose when you want the nginx reverse-proxy stack (same pattern as Ditto): + +```sh +git clone https://gitlab.com/soapbox-pub/agora-3.git +cd agora-3 +cp .env.example .env +docker compose up --build +``` + +Proxy URL: `http://localhost:8082` + +This starts: + +- `vite` service on the internal Docker network (`vite:8080`) +- `web` service (`nginx`) on host port `8082`, proxying to Vite with websocket support + +Stop stack: + +```sh +docker compose down +``` + +Production-style container build: + +```sh +docker compose -f docker-compose.prod.yml up --build +``` + ### Build ```sh diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 00000000..49374646 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,6 @@ +services: + web: + build: . + restart: unless-stopped + expose: + - "80" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..ed94a133 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,30 @@ +services: + web: + image: nginx:alpine + ports: + - "8082:80" + volumes: + - ./nginx.dev.conf:/etc/nginx/conf.d/default.conf:ro + - ./dist:/usr/share/nginx/html:ro + restart: unless-stopped + depends_on: + - vite + networks: + - agora-network + + vite: + image: node:22-alpine + working_dir: /app + # Use host node_modules so new dependencies are picked up after install. + command: sh -c "npm install && npm run dev" + volumes: + - .:/app + environment: + - NODE_ENV=development + networks: + - agora-network + restart: unless-stopped + +networks: + agora-network: + driver: bridge diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 00000000..9371c020 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,30 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript application/json; + + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/nginx.dev.conf b/nginx.dev.conf new file mode 100644 index 00000000..23aa3dee --- /dev/null +++ b/nginx.dev.conf @@ -0,0 +1,35 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript application/json; + + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + + location / { + resolver 127.0.0.11 valid=10s; + set $vite_backend http://vite:8080; + proxy_pass $vite_backend; + 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_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 86400; + } +}