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>
This commit is contained in:
Mistral Vibe
2026-04-01 14:55:10 +02:00
parent 69c614cf62
commit 16bfdd2e90
12 changed files with 2428 additions and 465 deletions

280
README.md Normal file
View File

@@ -0,0 +1,280 @@
# 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.