feat(M5): docker-compose full stack (caddy + node backend + sqlite)
docker-compose.yml: two profiles.
- backend: backend (node+ws+better-sqlite3, /data volume) + frontend
(Caddy static build, STORAGE=ws, same-origin proxy)
- firebase: existing Dockerfile + nginx (upstream path, untouched)
Run: docker compose --profile backend up --build. OrbStack local now,
remote docker context later.
server/Dockerfile: node:18-alpine, workspaces (shared dep), rebuild
better-sqlite3 for musl, DB at /data/tracker.sqlite.
Dockerfile.ws: CRA build STORAGE=ws → caddy:2-alpine serves /srv.
No backend URL baked (same-origin).
Caddyfile: handle /api/* + handle /ws → backend:4001 (path preserved,
mutually-exclusive handles so try_files SPA fallback never shadows proxy).
handle { static try_files } last. HTTP basic auth block optional.
src/storage/ws.js: same-origin defaults. Empty baseUrl = relative fetch
(Caddy proxy). wsUrl derives from window.location (http→ws/https→wss).
Fallback localhost for bare npm start dev.
.dockerignore: add data/ scratch/ tmp/ (never bake into image). Keep
Caddyfile in context (frontend build COPYs it).
Smoke verified via OrbStack:
- GET / → 200 (static SPA)
- PUT/GET /api/doc roundtrip → JSON persists
- WS /ws subscribe + change push → both work through proxy
Firebase profile: pre-existing Dockerfile requires .env.local (hardcoded
COPY on main, not changed here). User must create file. Not a regression.
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
# docker-compose.yml — two profiles:
|
||||
# firebase: existing Dockerfile (nginx + firebase build), upstream path
|
||||
# backend: full stack (caddy frontend + node backend + sqlite volume)
|
||||
#
|
||||
# Usage:
|
||||
# docker compose --profile backend up --build # full self-hosted stack
|
||||
# docker compose --profile firebase up --build # firebase-only (upstream)
|
||||
#
|
||||
# Run local in OrbStack; remote docker context later (just change context).
|
||||
|
||||
services:
|
||||
# ---- full self-hosted stack (STORAGE=ws) ----
|
||||
backend:
|
||||
profiles: ["backend"]
|
||||
build:
|
||||
context: .
|
||||
dockerfile: server/Dockerfile
|
||||
image: ttrpg-backend:local
|
||||
volumes:
|
||||
- backend-data:/data
|
||||
environment:
|
||||
- DB_PATH=/data/tracker.sqlite
|
||||
- PORT=4001
|
||||
# - CORS_ORIGIN=* # Caddy same-origin, cors not strictly needed
|
||||
expose:
|
||||
- "4001"
|
||||
restart: unless-stopped
|
||||
|
||||
frontend:
|
||||
profiles: ["backend"]
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.ws
|
||||
args:
|
||||
- REACT_APP_TRACKER_APP_ID=${TRACKER_APP_ID:-ttrpg-initiative-tracker-default}
|
||||
image: ttrpg-frontend:local
|
||||
ports:
|
||||
- "${FRONTEND_PORT:-8080}:80"
|
||||
depends_on:
|
||||
- backend
|
||||
# Optional basic auth: set in .env as CADDY_BASIC_AUTH="user <hashed-pass>"
|
||||
# Generate hash: caddy hash-password
|
||||
environment:
|
||||
- CADDY_BASIC_AUTH=${CADDY_BASIC_AUTH:-}
|
||||
restart: unless-stopped
|
||||
|
||||
# ---- firebase-only path (upstream, existing Dockerfile) ----
|
||||
firebase:
|
||||
profiles: ["firebase"]
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: ttrpg-firebase:local
|
||||
ports:
|
||||
- "${FRONTEND_PORT:-8080}:80"
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
backend-data:
|
||||
Reference in New Issue
Block a user