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:
280
README.md
Normal file
280
README.md
Normal 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: **~60–90 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.
|
||||
Reference in New Issue
Block a user