Initial commit: RehearsalHub POC

Full-stack self-hosted band rehearsal platform:

Backend (FastAPI + SQLAlchemy 2.0 async):
- Auth with JWT (register, login, /me, settings)
- Band management with Nextcloud folder integration
- Song management with audio version tracking
- Nextcloud scan to auto-import audio files
- Band membership with link-based invite system
- Song comments
- Audio analysis worker (BPM, key, loudness, waveform)
- Nextcloud activity watcher for auto-import
- WebSocket support for real-time annotation updates
- Alembic migrations (0001–0003)
- Repository pattern, Ruff + mypy configured

Frontend (React 18 + Vite + TypeScript strict):
- Login/register page with post-login redirect
- Home page with band list and creation form
- Band page with member panel, invite link, song list, NC scan
- Song page with waveform player, annotations, comment thread
- Settings page for per-user Nextcloud credentials
- Invite acceptance page (/invite/:token)
- ESLint v9 flat config + TypeScript strict mode

Infrastructure:
- Docker Compose: PostgreSQL, Redis, API, worker, watcher, nginx
- nginx reverse proxy for static files + /api/ proxy
- make check runs all linters before docker compose build

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Steffen Schuhmann
2026-03-28 21:53:03 +01:00
commit f7be1b994d
139 changed files with 12743 additions and 0 deletions

119
docker-compose.yml Normal file
View File

@@ -0,0 +1,119 @@
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB:-rehearsalhub}
POSTGRES_USER: ${POSTGRES_USER:-rh_user}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- pg_data:/var/lib/postgresql/data
networks:
- rh_net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-rh_user} -d ${POSTGRES_DB:-rehearsalhub}"]
interval: 5s
timeout: 5s
retries: 10
restart: unless-stopped
redis:
image: redis:7-alpine
command: redis-server --save 60 1 --loglevel warning
volumes:
- redis_data:/data
networks:
- rh_net
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
restart: unless-stopped
api:
build:
context: ./api
target: production
image: rehearsalhub/api:latest
environment:
DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-rh_user}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-rehearsalhub}
NEXTCLOUD_URL: ${NEXTCLOUD_URL}
NEXTCLOUD_USER: ${NEXTCLOUD_USER}
NEXTCLOUD_PASS: ${NEXTCLOUD_PASS}
REDIS_URL: redis://redis:6379/0
SECRET_KEY: ${SECRET_KEY}
DOMAIN: ${DOMAIN:-localhost}
networks:
- rh_net
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
audio-worker:
build:
context: ./worker
target: production
image: rehearsalhub/audio-worker:latest
environment:
DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-rh_user}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-rehearsalhub}
REDIS_URL: redis://redis:6379/0
NEXTCLOUD_URL: ${NEXTCLOUD_URL}
NEXTCLOUD_USER: ${NEXTCLOUD_USER}
NEXTCLOUD_PASS: ${NEXTCLOUD_PASS}
ANALYSIS_VERSION: "1.0.0"
volumes:
- audio_tmp:/tmp/audio
networks:
- rh_net
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
nc-watcher:
build:
context: ./watcher
target: production
image: rehearsalhub/nc-watcher:latest
environment:
NEXTCLOUD_URL: ${NEXTCLOUD_URL}
NEXTCLOUD_USER: ${NEXTCLOUD_USER}
NEXTCLOUD_PASS: ${NEXTCLOUD_PASS}
API_URL: http://api:8000
REDIS_URL: redis://redis:6379/0
POLL_INTERVAL: "30"
networks:
- rh_net
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
web:
build:
context: ./web
target: production
image: rehearsalhub/web:latest
ports:
- "8080:80"
networks:
- rh_net
depends_on:
- api
restart: unless-stopped
networks:
rh_net:
driver: bridge
volumes:
pg_data:
redis_data:
audio_tmp: