dow-prod-tracker/docker-compose.yml
DJP 0b0b3bfadc docker-compose: mount Box secret at /run/secrets/box-config.json
Short-form `- box-config` mounted the file at /run/secrets/box-config
(no extension), but BOX_CONFIG_FILE everywhere references the .json
suffix. Switch to long-form `target:` so the mount path matches the
configured env value and isBoxConfigured() reads the right file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:41:30 -04:00

110 lines
4.9 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: loreal-prod-tracker
services:
# ─── PostgreSQL with pgvector ───────────────────────────
db:
image: pgvector/pgvector:pg17
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres}
POSTGRES_DB: loreal_prod_tracker
# Host port is overridable via DB_HOST_PORT env var — deploy.sh auto-picks
# a free one if 5492 is taken on the host. The container-internal port
# (5432) never changes — the app connects to db:5432 over the Docker
# network and doesn't care what host port (if any) is mapped.
ports:
- "${DB_HOST_PORT:-5492}:5432"
volumes:
- pgdata:/var/lib/postgresql/data
- ./docker/db-init.sql:/docker-entrypoint-initdb.d/01-pgvector.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
# ─── Next.js app ───────────────────────────────────────
app:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
# Host port is overridable via APP_HOST_PORT env var — deploy.sh auto-picks
# a free one if 3002 is taken, and writes the chosen port into the Apache
# reverse-proxy config (apache/loreal-prod-tracker.conf) at the same time.
ports:
- "${APP_HOST_PORT:-3002}:3000"
environment:
# DATABASE_URL tuning knobs matter at ~40 concurrent users:
# connection_limit — how many pooled connections Prisma will
# open per app instance. Default is cpus*2+1 (~5-9 inside a
# container), which can run out at peak when mutations + query
# polling coincide. 20 gives plenty of headroom for this scale.
# pool_timeout — seconds to wait for a free connection before
# failing the request (default 10). 10s matches the default
# and stays explicit.
# Postgres side: default max_connections is 100, so 20 × a few
# app replicas is well below the ceiling.
DATABASE_URL: postgresql://postgres:${DB_PASSWORD:-postgres}@db:5432/loreal_prod_tracker?schema=public&connection_limit=20&pool_timeout=10
# Ollama — points to internal GPU server for embeddings + chat fallback
OLLAMA_HOST: ${OLLAMA_HOST:-http://10.24.42.219:11434}
OLLAMA_CHAT_HOST: ${OLLAMA_CHAT_HOST:-http://10.24.42.219:11434}
OLLAMA_CHAT_MODEL: ${OLLAMA_CHAT_MODEL:-gemma4:latest}
OLLAMA_EMBED_MODEL: ${OLLAMA_EMBED_MODEL:-nomic-embed-text}
NODE_ENV: production
AUTH_SECRET: ${AUTH_SECRET}
AUTH_TRUST_HOST: "true"
# Azure SPA registration — PKCE in browser, no client secret
AZURE_CLIENT_ID: ${AZURE_CLIENT_ID}
AZURE_TENANT_ID: ${AZURE_TENANT_ID}
AZURE_REDIRECT_URI: ${AZURE_REDIRECT_URI:-}
CRON_SECRET: ${CRON_SECRET:-change-me}
API_KEY: ${API_KEY:-}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
ANTHROPIC_MODEL: ${ANTHROPIC_MODEL:-}
DEV_BYPASS_AUTH: ${DEV_BYPASS_AUTH:-false}
DEV_USER_ID: ${DEV_USER_ID:-}
# OMG webhook (Shashank pending — stub until payload confirmed)
OMG_WEBHOOK_SECRET: ${OMG_WEBHOOK_SECRET:-}
OMG_WEBHOOK_ALLOW_INSECURE: ${OMG_WEBHOOK_ALLOW_INSECURE:-false}
# Auth: Entra SSO stays coded but gated. Flip to "true" post-MVP once redirect URI is live.
NEXT_PUBLIC_AUTH_ENTRA_ENABLED: ${NEXT_PUBLIC_AUTH_ENTRA_ENABLED:-false}
# Box integration (Phase 4). BOX_CONFIG_FILE points at the mounted
# JSON secret below. The other vars come from .env on the host.
BOX_CONFIG_FILE: ${BOX_CONFIG_FILE:-/run/secrets/box-config.json}
BOX_OUT_FOLDER_ID: ${BOX_OUT_FOLDER_ID:-}
BOX_WATCH_FOLDER_ID: ${BOX_WATCH_FOLDER_ID:-}
BOX_WEBHOOK_PRIMARY_KEY: ${BOX_WEBHOOK_PRIMARY_KEY:-}
BOX_WEBHOOK_SECONDARY_KEY: ${BOX_WEBHOOK_SECONDARY_KEY:-}
BOX_WEBHOOK_ALLOW_INSECURE: ${BOX_WEBHOOK_ALLOW_INSECURE:-false}
volumes:
- uploads_data:/data/uploads
secrets:
# Long-form so the secret lands at `/run/secrets/box-config.json` —
# matching BOX_CONFIG_FILE above. The default short-form would mount
# at `/run/secrets/box-config` (no extension), causing isBoxConfigured()
# to read from the wrong path.
- source: box-config
target: box-config.json
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:3000/api/health || exit 1"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
volumes:
pgdata:
uploads_data:
# Box JWT app config. Drop the JSON downloaded from the Box developer
# console at ./secrets/box-config.json on the host before `docker compose
# up`. Until that file exists, the app starts but Box features are
# disabled (isBoxConfigured() returns false; UI hides "Send to client").
secrets:
box-config:
file: ./secrets/box-config.json