Files
rehearshalhub/README.md
Mistral Vibe 16bfdd2e90 Move band management into dedicated settings pages
- Add BandSettingsPage (/bands/:id/settings/:panel) with Members,
  Storage, and Band Settings panels matching the mockup design
- Strip members list, invite controls, and NC folder config from
  BandPage — library view now focuses purely on recordings workflow
- Add band-scoped nav section to AppShell sidebar (Members, Storage,
  Band Settings) with correct per-panel active states
- Fix amAdmin bug: was checking if any member is admin; now correctly
  checks if the current user holds the admin role
- Add 31 vitest tests covering BandPage cleanliness, routing, access
  control (admin vs member), and per-panel mutation behaviour
- Add test:web, test:api:unit, test:feature (post-feature pipeline),
  and ci tasks to Taskfile; frontend tests run via podman node:20-alpine
- Add README with architecture overview, setup guide, and test docs
- Add @testing-library/dom and @testing-library/jest-dom to package.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 14:55:10 +02:00

281 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RehearsalHub
A web platform for bands to relisten to recorded rehearsals, drop timestamped comments, annotate moments, and collaborate asynchronously — all on top of your own storage (Nextcloud, Google Drive, S3, local).
---
## Architecture
```
┌─────────┐ HTTP/WS ┌──────────────┐ asyncpg ┌──────────┐
│ React │ ──────────► │ FastAPI │ ──────────► │ Postgres │
│ (Vite) │ │ (Python) │ └──────────┘
└─────────┘ └──────┬───────┘
│ Redis pub/sub
┌──────────┴──────────┐
│ │
┌──────▼──────┐ ┌──────────▼──────┐
│ Audio Worker │ │ NC Watcher │
│ (waveforms) │ │ (file polling) │
└─────────────┘ └─────────────────┘
```
| Service | Language | Purpose |
|---|---|---|
| `web` | TypeScript / React | UI — player, library, settings |
| `api` | Python / FastAPI | REST + WebSocket backend |
| `worker` | Python | Audio analysis, waveform generation |
| `watcher` | Python | Polls Nextcloud for new files |
| `db` | PostgreSQL 16 | Primary datastore |
| `redis` | Redis 7 | Task queue, pub/sub |
Files are **never copied** to RehearsalHub servers. The platform reads recordings directly from your own storage.
---
## Prerequisites
| Tool | Purpose | Install |
|---|---|---|
| **Podman** + `podman-compose` | Container runtime | [podman.io](https://podman.io) |
| **uv** | Python package manager (backend) | `curl -Lsf https://astral.sh/uv/install.sh \| sh` |
| **Task** | Task runner (`Taskfile.yml`) | [taskfile.dev](https://taskfile.dev) |
| **Node 20** | Frontend (runs inside podman — not needed locally) | via `podman run node:20-alpine` |
> Node is only required inside a container. All frontend commands pull `node:20-alpine` via podman automatically.
---
## Quick start
### 1. Configure environment
```bash
cp .env.example .env
# Edit .env — set SECRET_KEY, INTERNAL_SECRET, Nextcloud credentials, domain
```
Generate secrets:
```bash
openssl rand -hex 32 # paste as SECRET_KEY
openssl rand -hex 32 # paste as INTERNAL_SECRET
```
### 2. Start all services
```bash
task up # starts db, redis, api, audio-worker, nc-watcher, web (nginx)
task migrate # run database migrations
```
Or for first-time setup with Nextcloud scaffolding:
```bash
task setup # up + wait for NC + configure NC + seed data
```
### 3. Open the app
Visit `http://localhost:8080` (or your configured `DOMAIN`).
---
## Development
Start the backend with hot reload and mount source directories:
```bash
task dev:detach # start db, redis, api, worker, watcher in dev mode (background)
task dev:web # start Vite dev server at http://localhost:3000 (proxies /api)
```
Or run both together:
```bash
task dev # foreground, streams all logs
```
Follow logs:
```bash
task logs # all services
task dev:logs SERVICE=api # single service
```
Restart a single service after a code change:
```bash
task dev:restart SERVICE=api
```
### Database migrations
```bash
# Apply pending migrations
task migrate
# Create a new migration from model changes
task migrate:auto M="add instrument field to band_member"
```
### Useful shells
```bash
task shell:api # bash in the API container
task shell:db # psql
task shell:redis # redis-cli
```
---
## Testing
### After every feature — run this
```bash
task test:feature
```
This runs the full **post-feature pipeline** (no external services required):
| Step | What it checks |
|---|---|
| `typecheck:web` | TypeScript compilation errors |
| `test:web` | React component tests (via podman + vitest) |
| `test:api:unit` | Python unit tests (no DB needed) |
| `test:worker` | Worker unit tests |
| `test:watcher` | Watcher unit tests |
Typical runtime: **~6090 seconds**.
---
### Full CI pipeline
Runs everything including integration tests against a live database.
**Requires services to be up** (`task dev:detach && task migrate`).
```bash
task ci
```
Stages:
```
lint ──► typecheck ──► test:web ──► test:api (unit + integration)
──► test:worker
──► test:watcher
```
---
### Individual test commands
```bash
# Frontend
task test:web # React/vitest tests (podman, no local Node needed)
task typecheck:web # TypeScript type check only
# Backend — unit (no services required)
task test:api:unit # API unit tests
task test:worker # Worker tests
task test:watcher # Watcher tests
# Backend — all (requires DB + services)
task test:api # unit + integration tests with coverage
task test # all backend suites
# Integration only
task test:integration # API integration tests (DB required)
# Lint
task lint # ruff + mypy (Python), eslint (TS)
task format # auto-format Python with ruff
```
---
### Frontend test details
Frontend tests run inside a `node:20-alpine` container via podman and do not require Node installed on the host:
```bash
task test:web
# equivalent to:
podman run --rm -v ./web:/app:Z -w /app node:20-alpine \
sh -c "npm install --legacy-peer-deps --silent && npm run test"
```
Tests use **vitest** + **@testing-library/react** and are located alongside the source files they test:
```
web/src/pages/
BandPage.tsx
BandPage.test.tsx ← 7 tests: library view cleanliness
BandSettingsPage.tsx
BandSettingsPage.test.tsx ← 24 tests: routing, access control, mutations
web/src/test/
setup.ts ← jest-dom matchers
helpers.tsx ← QueryClient + MemoryRouter wrapper
```
---
## Project structure
```
rehearshalhub/
├── api/ Python / FastAPI backend
│ ├── src/rehearsalhub/
│ │ ├── routers/ HTTP endpoints
│ │ ├── models/ SQLAlchemy ORM models
│ │ ├── repositories/ DB access layer
│ │ ├── services/ Business logic
│ │ └── schemas/ Pydantic request/response schemas
│ └── tests/
│ ├── unit/ Pure unit tests (no DB)
│ └── integration/ Full HTTP tests against a real DB
├── web/ TypeScript / React frontend
│ └── src/
│ ├── api/ API client functions
│ ├── components/ Shared components (AppShell, etc.)
│ ├── pages/ Route-level page components
│ └── test/ Test helpers and setup
├── worker/ Audio analysis service (Python)
├── watcher/ Nextcloud file polling service (Python)
├── scripts/ nc-setup.sh, seed.sh
├── traefik/ Reverse proxy config
├── docker-compose.yml Production compose
├── docker-compose.dev.yml Dev overrides (hot reload, source mounts)
├── Taskfile.yml Task runner (preferred)
└── Makefile Makefile aliases (same targets)
```
---
## Key design decisions
- **Storage is always yours.** RehearsalHub never copies audio files. It reads them directly from Nextcloud (or other providers) on demand.
- **Date is the primary axis.** The library groups recordings by session date. Filters narrow within that structure — they never flatten it.
- **Band switching is tenant-level.** Switching bands re-scopes the library, settings, and all band-specific views.
- **Settings are band-scoped.** Member management, storage configuration, and band identity live at `/bands/:id/settings`, not in the library view.
---
## Environment variables
| Variable | Required | Description |
|---|---|---|
| `SECRET_KEY` | ✅ | 32-byte hex, JWT signing key |
| `INTERNAL_SECRET` | ✅ | 32-byte hex, service-to-service auth |
| `DATABASE_URL` | ✅ | PostgreSQL connection string |
| `REDIS_URL` | ✅ | Redis connection string |
| `NEXTCLOUD_URL` | ✅ | Full URL to your Nextcloud instance |
| `NEXTCLOUD_USER` | ✅ | Nextcloud service account username |
| `NEXTCLOUD_PASS` | ✅ | Nextcloud service account password |
| `DOMAIN` | ✅ | Public domain (used by Traefik TLS) |
| `ACME_EMAIL` | ✅ | Let's Encrypt email |
| `POSTGRES_DB` | ✅ | Database name |
| `POSTGRES_USER` | ✅ | Database user |
| `POSTGRES_PASSWORD` | ✅ | Database password |
See `.env.example` for the full template.