ad48829337
The dual backend (SQLite via db.js + Postgres via schema.pg.sql) was a maintenance foot-gun: a schema change could land on the SQLite path only and silently 500 every read on prod (it just did, with #18/#13). Production has run on Postgres for weeks, so SQLite is retired: ONE schema source of truth (db/schema.pg.sql), no drift possible. - dbx.js: default DB_BACKEND=pg; an unknown backend now fails loudly at require time instead of silently selecting a stale engine. - Deleted server/db.js, server/db/sqlite.js, server/db/migrate-sqlite-to-pg.js, server/scripts/migrate-bizgaze-only.js (all SQLite-only, none in the runtime path — the running server loads db/pg.js). - Tests (e2e, db-smoke) target Postgres now and fail-fast (skip) unless DATABASE_URL points at a disposable test DB — never SQLite, never prod. - Removed the dead DB_PATH env + fixed misleading SQLite comments in the Dockerfile / docker-compose (kept the /data volume: it holds uploads/recordings/transcripts/downloads, not just the old data.db). - CLAUDE.md: stack + repo-layout + run-locally updated for Postgres-only. Runtime is unaffected (prod already sets DB_BACKEND=pg and pg is a prod dep); this only removes the unused SQLite path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
113 lines
5.3 KiB
YAML
113 lines
5.3 KiB
YAML
# BizGaze Support — deployed behind the existing Nginx Proxy Manager.
|
|
# No host ports are published: NPM reaches this container by name on the
|
|
# shared `nginx_proxy_manager_default` network. TLS is terminated by NPM,
|
|
# which proxies https://remote.bizgaze.com -> bizgaze-support:8090.
|
|
services:
|
|
app:
|
|
build: .
|
|
image: bizgaze-support:latest
|
|
container_name: bizgaze-support
|
|
restart: unless-stopped
|
|
environment:
|
|
- PORT=8090
|
|
# Desktop installers + auto-update feed live on the persistent volume so uploaded
|
|
# builds survive image rebuilds (a plain image path would be wiped on every deploy).
|
|
- DOWNLOADS_DIR=/data/downloads
|
|
# Chat uploads / recordings / transcripts on the persistent volume too, so they survive image
|
|
# rebuilds (otherwise old shared images 404 as "broken image" after every deploy).
|
|
- UPLOADS_DIR=/data/uploads
|
|
# Max chat-attachment size in MB (default 1024 = 1 GB). The app streams uploads to /data/uploads, so
|
|
# large files don't buffer in memory. IMPORTANT: also set Nginx Proxy Manager's client_max_body_size
|
|
# for remote.bizgaze.com to at least this (Advanced tab: `client_max_body_size 1024m;`) or the proxy
|
|
# rejects big uploads before they reach the app.
|
|
- MAX_UPLOAD_MB=1024
|
|
- REC_DIR=/data/recordings
|
|
- TRANS_DIR=/data/transcripts
|
|
# Secrets (TURN credentials, SSO_SECRET, BIZGAZE_WEBHOOK_URL, etc.) live in
|
|
# a .env file next to this compose file. It is gitignored — never committed.
|
|
# See .env.example for the expected keys.
|
|
env_file:
|
|
- path: .env
|
|
required: false
|
|
volumes:
|
|
- bizgaze_support_data:/data # persists uploads / recordings / transcripts / downloads across rebuilds
|
|
networks:
|
|
- npm
|
|
# The DB is Postgres (SQLite retired 2026-08-12), so wait for it to be healthy before the app starts —
|
|
# otherwise the first queries race the DB coming up.
|
|
depends_on:
|
|
bizgazepg:
|
|
condition: service_healthy
|
|
|
|
# PostgreSQL — the app's data store (DB_BACKEND=pg; the only backend since SQLite was retired). Distinct
|
|
# service/container name so it never collides with the other postgres containers on the shared NPM
|
|
# network; the app reaches it as `bizgaze-postgres`. Data on its own named volume.
|
|
bizgazepg:
|
|
image: postgres:16
|
|
container_name: bizgaze-postgres
|
|
restart: unless-stopped
|
|
environment:
|
|
- POSTGRES_USER=bizgaze
|
|
- POSTGRES_DB=bizgaze
|
|
# Password comes from the same .env as the app (compose interpolates ${...} from the .env in this dir).
|
|
# NOTE: Postgres only applies POSTGRES_PASSWORD on FIRST init of an empty data volume.
|
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-bizgaze_local}
|
|
volumes:
|
|
- bizgaze_pg_data:/var/lib/postgresql/data
|
|
networks:
|
|
- npm
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U bizgaze -d bizgaze"]
|
|
interval: 5s
|
|
timeout: 3s
|
|
retries: 12
|
|
|
|
# Redis — cross-instance real-time fan-out (chat/presence), used only when PUBSUB_BACKEND=redis. Dormant
|
|
# by default (behind the 'scale' profile, like livekit), so a normal deploy never starts it and the app
|
|
# stays single-instance on the in-memory pubsub. To run multiple app instances: start this
|
|
# (`docker compose --profile scale up -d`), set PUBSUB_BACKEND=redis + REDIS_URL in .env, and put the app
|
|
# behind a load balancer with sticky sessions for the /ws WebSocket. (Meeting SIGNALING state is still
|
|
# per-process — cross-instance meetings need sticky routing or further work; chat/presence fan out here.)
|
|
bizgazeredis:
|
|
image: redis:7-alpine
|
|
container_name: bizgaze-redis
|
|
restart: unless-stopped
|
|
profiles: ["scale"]
|
|
networks:
|
|
- npm
|
|
|
|
# LiveKit SFU — meeting media server. Optional: only started/used when the app's .env has
|
|
# LIVEKIT_URL/API_KEY/API_SECRET set (otherwise meetings use the built-in P2P mesh). NPM proxies
|
|
# wss://livekit.bizgaze.com -> livekit:7880 (signaling); media flows over the published UDP/TCP
|
|
# ports below, NOT through NPM. Single-node (no Redis) — consistent with the app's single-instance rule.
|
|
livekit:
|
|
# v1.8+ implements the /rtc/v1 signaling path (protocol 17) that the bundled
|
|
# livekit-client@2.20 uses. On the older v1.7 the client fell back to the legacy path and
|
|
# track publishing broke (mic/cam wouldn't turn on). Keep this within one minor of the client.
|
|
image: livekit/livekit-server:v1.9
|
|
container_name: bizgaze-livekit
|
|
restart: unless-stopped
|
|
# Dormant by default: a normal `docker compose up -d` / deploy.sh does NOT start it. Enable SFU
|
|
# explicitly with `docker compose --profile sfu up -d` after setting the LIVEKIT_* vars (see DEPLOY.md).
|
|
profiles: ["sfu"]
|
|
command: --config /etc/livekit.yaml
|
|
environment:
|
|
# key: secret, sourced from the same .env as the app so both sign/verify with the same secret.
|
|
- "LIVEKIT_KEYS=${LIVEKIT_API_KEY}: ${LIVEKIT_API_SECRET}"
|
|
volumes:
|
|
- ./livekit.yaml:/etc/livekit.yaml:ro
|
|
ports:
|
|
- "7881:7881" # WebRTC over TCP (fallback)
|
|
- "50000:50000/udp" # single WebRTC media UDP port (must match livekit.yaml rtc.udp_port)
|
|
networks:
|
|
- npm
|
|
|
|
networks:
|
|
npm:
|
|
external: true
|
|
name: nginx_proxy_manager_default
|
|
|
|
volumes:
|
|
bizgaze_support_data:
|
|
bizgaze_pg_data:
|