Tournament engine (pools, single/double elimination, standings with proper tiebreaks, withdrawals, two-court queue with ETAs and alerts), a single-process Node server with SQLite via node:sqlite and a WebSocket live board, organizer desk, QR landing page that follows the tournament phase, Dockerfile and compose with cloudflared, and documentation for deploying and running a day. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01MB7nCCAscYsb3zzkT6LHZi
36 lines
1022 B
YAML
36 lines
1022 B
YAML
# Courtside behind a Cloudflare Tunnel.
|
|
# cp .env.example .env # set ADMIN_PASSWORD, SESSION_SECRET, CLOUDFLARE_TUNNEL_TOKEN
|
|
# docker compose up -d
|
|
# The app publishes no ports to the host: the only way in is through the tunnel
|
|
# (cloudflared reaches it on the compose network as http://app:3000).
|
|
# For local testing without a tunnel, use docker-compose.local.yml instead.
|
|
services:
|
|
app:
|
|
build: .
|
|
image: courtside:latest
|
|
restart: unless-stopped
|
|
env_file: .env
|
|
environment:
|
|
- PORT=3000
|
|
- DB_PATH=/data/courtside.sqlite
|
|
volumes:
|
|
- courtside-data:/data
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/healthz"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
cloudflared:
|
|
image: cloudflare/cloudflared:latest
|
|
restart: unless-stopped
|
|
command: tunnel --no-autoupdate run
|
|
environment:
|
|
- TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN}
|
|
depends_on:
|
|
app:
|
|
condition: service_healthy
|
|
|
|
volumes:
|
|
courtside-data:
|