The sw.js and workbox-*.js files were being caught by the immutable static asset regex (expires 1y), causing stale service workers and potential 502 errors during re-registration. Use location = /sw.js (exact match, highest Nginx priority) and a regex for workbox files that appears before the asset cache block. Also removes the dead duplicate location blocks at the bottom of the config that were never reached due to regex priority. Co-authored-by: Cursor <cursoragent@cursor.com>
230 lines
7.9 KiB
YAML
230 lines
7.9 KiB
YAML
# ═══════════════════════════════════════════════════════════════
|
|
# IndeeHub — Production Stack for Portainer
|
|
# ═══════════════════════════════════════════════════════════════
|
|
#
|
|
# All ${VARIABLES} are resolved by Portainer at deploy time.
|
|
# Configure them in Portainer → Stacks → Environment variables
|
|
# before deploying.
|
|
#
|
|
# See env.portainer for the full list of required variables.
|
|
#
|
|
# For local development, use: docker compose -f docker-compose.dev.yml up
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
version: '3.8'
|
|
|
|
services:
|
|
# ── Frontend (nginx serving built Vue app) ──────────────────
|
|
app:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile
|
|
args:
|
|
CACHEBUST: "15"
|
|
VITE_USE_MOCK_DATA: "false"
|
|
VITE_CONTENT_ORIGIN: ${FRONTEND_URL}
|
|
VITE_INDEEHUB_API_URL: /api
|
|
VITE_INDEEHUB_CDN_URL: /storage
|
|
VITE_NOSTR_RELAYS: ""
|
|
restart: unless-stopped
|
|
ports:
|
|
- "${APP_PORT:-7777}:7777"
|
|
depends_on:
|
|
- relay
|
|
- api
|
|
networks:
|
|
- indeedhub-network
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:7777/health"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 40s
|
|
|
|
# ── Backend API (NestJS) ────────────────────────────────────
|
|
api:
|
|
build:
|
|
context: ./backend
|
|
dockerfile: Dockerfile
|
|
args:
|
|
CACHEBUST: "10"
|
|
restart: unless-stopped
|
|
environment:
|
|
# ── Core ─────────────────────────────────────────────
|
|
ENVIRONMENT: production
|
|
PORT: 4000
|
|
DOMAIN: ${DOMAIN}
|
|
FRONTEND_URL: ${FRONTEND_URL}
|
|
|
|
# ── Database ─────────────────────────────────────────
|
|
DATABASE_HOST: postgres
|
|
DATABASE_PORT: 5432
|
|
DATABASE_USER: ${POSTGRES_USER}
|
|
DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
|
|
DATABASE_NAME: ${POSTGRES_DB}
|
|
|
|
# ── Redis / BullMQ ───────────────────────────────────
|
|
QUEUE_HOST: redis
|
|
QUEUE_PORT: 6379
|
|
QUEUE_PASSWORD: ${REDIS_PASSWORD:-}
|
|
|
|
# ── S3 / MinIO ──────────────────────────────────────
|
|
S3_ENDPOINT: http://minio:9000
|
|
AWS_REGION: us-east-1
|
|
AWS_ACCESS_KEY: ${S3_ACCESS_KEY}
|
|
AWS_SECRET_KEY: ${S3_SECRET_KEY}
|
|
S3_PRIVATE_BUCKET_NAME: indeedhub-private
|
|
S3_PUBLIC_BUCKET_NAME: indeedhub-public
|
|
S3_PUBLIC_BUCKET_URL: ${S3_PUBLIC_BUCKET_URL}
|
|
|
|
# ── BTCPay Server ───────────────────────────────────
|
|
BTCPAY_URL: ${BTCPAY_URL}
|
|
BTCPAY_API_KEY: ${BTCPAY_API_KEY}
|
|
BTCPAY_STORE_ID: ${BTCPAY_STORE_ID}
|
|
BTCPAY_WEBHOOK_SECRET: ${BTCPAY_WEBHOOK_SECRET}
|
|
|
|
# ── Nostr Auth / JWT ─────────────────────────────────
|
|
NOSTR_JWT_SECRET: ${NOSTR_JWT_SECRET}
|
|
NOSTR_JWT_EXPIRES_IN: ${NOSTR_JWT_EXPIRES_IN:-7d}
|
|
|
|
# ── AES-128 Content Encryption ──────────────────────
|
|
AES_MASTER_SECRET: ${AES_MASTER_SECRET}
|
|
|
|
# ── Admin API ────────────────────────────────────────
|
|
ADMIN_API_KEY: ${ADMIN_API_KEY:-}
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_started
|
|
minio:
|
|
condition: service_started
|
|
networks:
|
|
- indeedhub-network
|
|
healthcheck:
|
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4000/nostr-auth/health"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 5
|
|
start_period: 60s
|
|
|
|
# ── PostgreSQL Database ─────────────────────────────────────
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_USER: ${POSTGRES_USER}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
POSTGRES_DB: ${POSTGRES_DB}
|
|
volumes:
|
|
- postgres-data:/var/lib/postgresql/data
|
|
networks:
|
|
- indeedhub-network
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U indeedhub"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 30s
|
|
|
|
# ── Redis (BullMQ job queue) ────────────────────────────────
|
|
redis:
|
|
image: redis:7-alpine
|
|
restart: unless-stopped
|
|
command: >
|
|
sh -c "if [ -n '${REDIS_PASSWORD:-}' ]; then
|
|
redis-server --requirepass '${REDIS_PASSWORD}' --appendonly yes;
|
|
else
|
|
redis-server --appendonly yes;
|
|
fi"
|
|
volumes:
|
|
- redis-data:/data
|
|
networks:
|
|
- indeedhub-network
|
|
|
|
# ── MinIO (S3-compatible object storage) ────────────────────
|
|
minio:
|
|
image: minio/minio:latest
|
|
restart: unless-stopped
|
|
command: server /data --console-address ":9001"
|
|
environment:
|
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
|
volumes:
|
|
- minio-data:/data
|
|
networks:
|
|
- indeedhub-network
|
|
|
|
# ── MinIO bucket init (one-shot: creates required buckets) ──
|
|
minio-init:
|
|
image: minio/mc:latest
|
|
depends_on:
|
|
- minio
|
|
entrypoint: >
|
|
/bin/sh -c "
|
|
sleep 5;
|
|
mc alias set local http://minio:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD};
|
|
mc mb local/indeedhub-private --ignore-existing;
|
|
mc mb local/indeedhub-public --ignore-existing;
|
|
mc anonymous set download local/indeedhub-public;
|
|
echo 'MinIO buckets initialized';
|
|
"
|
|
networks:
|
|
- indeedhub-network
|
|
restart: "no"
|
|
|
|
# ── FFmpeg Transcoding Worker ───────────────────────────────
|
|
ffmpeg-worker:
|
|
build:
|
|
context: ./backend
|
|
dockerfile: Dockerfile.ffmpeg
|
|
args:
|
|
CACHEBUST: "12"
|
|
restart: unless-stopped
|
|
environment:
|
|
ENVIRONMENT: production
|
|
DATABASE_HOST: postgres
|
|
DATABASE_PORT: 5432
|
|
DATABASE_USER: ${POSTGRES_USER}
|
|
DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
|
|
DATABASE_NAME: ${POSTGRES_DB}
|
|
QUEUE_HOST: redis
|
|
QUEUE_PORT: 6379
|
|
QUEUE_PASSWORD: ${REDIS_PASSWORD:-}
|
|
S3_ENDPOINT: http://minio:9000
|
|
AWS_REGION: us-east-1
|
|
AWS_ACCESS_KEY: ${S3_ACCESS_KEY}
|
|
AWS_SECRET_KEY: ${S3_SECRET_KEY}
|
|
S3_PRIVATE_BUCKET_NAME: indeedhub-private
|
|
S3_PUBLIC_BUCKET_NAME: indeedhub-public
|
|
S3_PUBLIC_BUCKET_URL: ${S3_PUBLIC_BUCKET_URL}
|
|
AES_MASTER_SECRET: ${AES_MASTER_SECRET}
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_started
|
|
minio:
|
|
condition: service_started
|
|
networks:
|
|
- indeedhub-network
|
|
|
|
# ── Nostr Relay ─────────────────────────────────────────────
|
|
relay:
|
|
image: scsibug/nostr-rs-relay:latest
|
|
restart: unless-stopped
|
|
volumes:
|
|
- relay-data:/usr/src/app/db
|
|
networks:
|
|
- indeedhub-network
|
|
|
|
networks:
|
|
indeedhub-network:
|
|
driver: bridge
|
|
|
|
volumes:
|
|
postgres-data:
|
|
redis-data:
|
|
minio-data:
|
|
relay-data:
|