feat: initial scaffold for ClickTrack monorepo
Full self-hosted click track generator for cover bands. Core technical pieces implemented: - CTP (Click Track Protocol) TypeScript schema, Zod validator, and WAV renderer (44.1 kHz, 16-bit PCM, accented downbeats, ramp sections) - MusicBrainz API client with 1 req/s rate limiting - PostgreSQL schema (songs, tempo_maps, registry_sync_log) with triggers - Git registry sync logic (clone/pull → validate CTP → upsert DB) - Next.js 14 App Router: search page, track page, API routes (/api/songs, /api/tracks, /api/generate) - UI components: SearchBar, SongResult, TempoMapEditor, ClickTrackPlayer (Web Audio API in-browser playback + WAV download) - Docker Compose stack: app + postgres + redis + nginx + registry-sync - Multi-stage Dockerfile with standalone Next.js output - .env.example documenting all configuration variables - README with setup instructions, CTP format spec, and API reference Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
121
docker-compose.yml
Normal file
121
docker-compose.yml
Normal file
@@ -0,0 +1,121 @@
|
||||
version: "3.9"
|
||||
|
||||
# ClickTrack — full self-hosted stack
|
||||
# Usage: docker compose up -d
|
||||
# First run: docker compose up -d --build
|
||||
|
||||
services:
|
||||
|
||||
# ── Next.js application ──────────────────────────────────────────────────────
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DATABASE_URL: postgres://clicktrack:${POSTGRES_PASSWORD:-clicktrack}@postgres:5432/clicktrack
|
||||
REDIS_URL: redis://redis:6379
|
||||
REGISTRY_REPO: ${REGISTRY_REPO:-}
|
||||
REGISTRY_BRANCH: ${REGISTRY_BRANCH:-main}
|
||||
NEXT_PUBLIC_APP_NAME: ${NEXT_PUBLIC_APP_NAME:-ClickTrack}
|
||||
MUSICBRAINZ_USER_AGENT: ${MUSICBRAINZ_USER_AGENT:-ClickTrack/0.1 (self-hosted)}
|
||||
NODE_ENV: production
|
||||
expose:
|
||||
- "3000"
|
||||
networks:
|
||||
- internal
|
||||
|
||||
# ── PostgreSQL 16 ─────────────────────────────────────────────────────────────
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: clicktrack
|
||||
POSTGRES_USER: clicktrack
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-clicktrack}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./docker/init.sql:/docker-entrypoint-initdb.d/01-init.sql:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U clicktrack -d clicktrack"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
- internal
|
||||
|
||||
# ── Redis 7 ───────────────────────────────────────────────────────────────────
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
command: redis-server --save 60 1 --loglevel warning
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 10
|
||||
networks:
|
||||
- internal
|
||||
|
||||
# ── Nginx reverse proxy ───────────────────────────────────────────────────────
|
||||
nginx:
|
||||
image: nginx:1.27-alpine
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- app
|
||||
ports:
|
||||
- "${HTTP_PORT:-80}:80"
|
||||
- "${HTTPS_PORT:-443}:443"
|
||||
volumes:
|
||||
- ./docker/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
# Mount TLS certificates here for HTTPS (optional):
|
||||
# - /etc/letsencrypt:/etc/letsencrypt:ro
|
||||
networks:
|
||||
- internal
|
||||
|
||||
# ── Registry sync (optional cron container) ───────────────────────────────────
|
||||
registry-sync:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DATABASE_URL: postgres://clicktrack:${POSTGRES_PASSWORD:-clicktrack}@postgres:5432/clicktrack
|
||||
REDIS_URL: redis://redis:6379
|
||||
REGISTRY_REPO: ${REGISTRY_REPO:-}
|
||||
REGISTRY_BRANCH: ${REGISTRY_BRANCH:-main}
|
||||
NODE_ENV: production
|
||||
# Run the sync script on a cron schedule inside the container.
|
||||
# Requires REGISTRY_REPO to be set; the container exits cleanly if not.
|
||||
command: >
|
||||
sh -c '
|
||||
while true; do
|
||||
node -e "
|
||||
const { syncRegistry } = require(\"./lib/registry/sync\");
|
||||
syncRegistry().then(r => console.log(\"Sync:\", JSON.stringify(r))).catch(console.error);
|
||||
" || true;
|
||||
sleep ${REGISTRY_SYNC_INTERVAL:-3600};
|
||||
done
|
||||
'
|
||||
profiles:
|
||||
- registry
|
||||
networks:
|
||||
- internal
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
||||
|
||||
networks:
|
||||
internal:
|
||||
driver: bridge
|
||||
Reference in New Issue
Block a user