- Add scan_manager: background asyncio task + Redis event store so scans
survive UI navigation; SSE stream reads from Redis and is reconnectable
- Replace SSE-tied scan endpoint with POST /nc-scan/start + GET /nc-scan/stream
- Fix frontend: AbortController + useEffect cleanup cancels stream on unmount
without stopping the server-side scan
- Add unique constraint on audio_versions.nc_file_path (migration 0009) to
prevent duplicate imports from concurrent scans; handle IntegrityError
gracefully in nc_scan with rollback + skip
- Fix API health check: use plain python instead of uv (not in dev image)
- Optimize Taskfile: fix duplicate dev:restart, add dev:fresh/dev:rebuild/
dev:status, migrate uses run --rm, check includes typecheck
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces per-member Nextcloud credentials with a BandStorage model that
supports multiple providers. Credentials are Fernet-encrypted at rest;
worker receives audio via an internal streaming endpoint instead of
direct storage access.
- Add BandStorage DB model with partial unique index (one active per band)
- Add migrations 0007 (create band_storage) and 0008 (drop old nc columns)
- Add StorageFactory that builds the correct StorageClient from BandStorage
- Add storage router: connect/nextcloud, OAuth2 authorize/callback, list, disconnect
- Add Fernet encryption helpers in security/encryption.py
- Rewrite watcher for per-band polling via internal API config endpoint
- Update worker to stream audio from API instead of accessing storage directly
- Update frontend: new storage API in bands.ts, rewritten StorageSection,
simplified band creation modal (no storage step)
- Add STORAGE_ENCRYPTION_KEY to all docker-compose files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Added INTERNAL_SECRET environment variable to the api service in docker-compose.yml.
This fixes the pydantic validation error that was causing the API to fail to start.
The INTERNAL_SECRET is required for internal service-to-service communication
(nc-watcher → API) and was missing from the container environment.