Commit Graph

114 Commits

Author SHA1 Message Date
Mistral Vibe
b72cdf0bd3 Add detailed error analysis for 403 issues
- Identified root cause: list_invites endpoint requires admin role
- Should allow regular members to see invites
- Found bug in bands.py line 33
- Includes recommended fixes and action plan

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 12:33:15 +02:00
Mistral Vibe
e6fb64e161 Add comprehensive project summary document
- Complete summary of band invitation system implementation
- Captures all phases: analysis, backend, frontend, testing
- Documents technical decisions, file changes, and current state
- Includes unresolved issues and next steps

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 12:26:45 +02:00
Mistral Vibe
5d7c613317 Component for invitations 2026-04-01 12:12:26 +02:00
Mistral Vibe
d61772207e Fix remaining TS6133 errors
- invites.ts: Remove unused bandId from listNonMemberUsers
- InviteManagement.tsx: Remove unused code (useEffect, queryClient, isRefreshing)

All TypeScript errors resolved!

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 11:55:38 +02:00
Mistral Vibe
1280020f83 Temporarily remove UserSearch - needs backend support
UserSearch component disabled until backend endpoint for listing non-member users is implemented.

Currently MVP includes:
- Backend APIs: list invites, revoke invites, get invite info 
- Frontend: InviteManagement component (list + revoke) 
- Note: UserSearch (admin search) disabled - needs backend support

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 11:53:30 +02:00
Mistral Vibe
2aa8ec8c59 Fix TypeScript build errors
- Remove unused imports in invites.ts
- Fix InviteManagement component (remove unused props, unneeded code)
- Fix BandPage.tsx (remove currentMemberId, remove UserSearch for now)
- Remove unused imports in types/invite.ts

Build errors resolved:
- TS6133: unused variables
- TS2304: missing variables
- TS2307: module not found

Note: UserSearch temporarily disabled - needs backend support for listing non-members

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 11:53:00 +02:00
Mistral Vibe
81c90222d5 Phase 2 frontend: Add React components for band invite management
Components created:
- InviteManagement.tsx: List pending invites, revoke functionality, copy links
- UserSearch.tsx: Search users to invite, role selection
- web/src/api/invites.ts: API wrappers for new endpoints
- web/src/types/invites.ts: TypeScript interfaces

UI enhancements:
- BandPage.tsx: Integrated new components, admin-only sections
- Members section now includes invite management for admins
- Search component for finding users to invite

Features:
- Admin can list, view, and revoke pending invites
- Copy invite links to clipboard
- Search existing users to invite (excluding current members)
- Real-time invite status (pending/expired/used)

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 11:48:14 +02:00
Mistral Vibe
50622c7bf7 Add verification summary for Phase 1 backend implementation
- Summary of all changes made
- Syntax verification results
- Test coverage details
- API endpoint documentation
- Security considerations
- Metrics and checklist

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 11:43:38 +02:00
Mistral Vibe
56ffd98f5e Phase 1 backend implementation: Add invite management endpoints
Implements core invite management features for band admins:
- GET /bands/{band_id}/invites - List all invites for a band (admin only)
- DELETE /invites/{invite_id} - Revoke pending invite (admin only)
- GET /invites/{token}/info - Get invite details (public)

Backend changes:
- Add invites router with 3 endpoints
- Update BandRepository with get_invites_for_band and get_invite_by_id methods
- Add new schemas for invite listing and info
- Register invites router in main.py

Tests:
- Integration tests for all 3 endpoints
- Permission tests (admin vs non-admin)
- Edge cases (not found, expired, etc.)

This addresses the core requirements:
- Admins can see pending invites
- Admins can revoke pending invites
- Users can view invite details before accepting

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 11:30:52 +02:00
Mistral Vibe
ce228919df Add comprehensive band invitation system analysis and implementation plan
- Deep dive into existing band invitation implementation
- Identified gaps in current system (invite listing, revocation, user search)
- Created detailed architecture analysis and design options
- Documented comprehensive implementation plan with phases
- Includes backend endpoints, frontend components, and testing strategy

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 11:26:08 +02:00
Mistral Vibe
4af19ed93b Updates 2026-04-01 09:56:19 +02:00
Mistral Vibe
6dc191585c style: align all pages with CLAUDE.md design system
- Inputs: uniform padding (8px 12px), borderRadius 7, bg-inset background
- List rows/cards: bg-subtle background, border-subtle border (bg-inset was input-only)
- Invite/admin badge borders: use accent-border var instead of raw accent
- Section headers: 11px, weight 500, uppercase, 0.7px letter-spacing
- Notification/status banners: borderRadius 8
- Remove debug console.log statements from SettingsPage avatar upload flow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:52:06 +02:00
Mistral Vibe
7677677e7b Merge branch 'feature/ui-ux-improvements'
Adds persistent sidebar shell and fixes nginx/auth bugs.
2026-04-01 09:43:57 +02:00
Mistral Vibe
d9035acdff feat: app shell with sidebar + bug fixes
UI:
- Add persistent sidebar (210px) with band switcher dropdown, Library/Player/Settings nav, user avatar row, and sign-out button
- Align design system CSS vars to CLAUDE.md spec (#0f0f12 bg, #e8a22a amber accent, rgba borders/text)
- Remove light mode toggle (no light mode in v1)
- Homepage auto-redirects to first band; shows create-band form only when no bands exist
- Strip full-page wrappers from all pages (shell owns layout)
- Remove debug console.log statements from SongPage

Bug fixes:
- nginx: trailing slash on `location ^~ /api/v1/bands/` caused 301 redirect on POST, dropping the request body — removed trailing slash
- API: _member_from_request (used by nc-scan stream) only accepted Bearer token, not httpOnly cookie — add rh_token cookie fallback
- API: internal_secret config field now has a dev default so the service starts without INTERNAL_SECRET env var set

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:43:47 +02:00
Mistral Vibe
ae7bf96dc1 fix: add INTERNAL_SECRET to api environment variables
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.
2026-04-01 09:01:25 +02:00
Mistral Vibe
c1941ed9ac security: httpOnly cookies, rate limiting, nginx headers, SSE sanitization
Auth / token storage:
- JWT is now set as an httpOnly Secure SameSite=Lax cookie on login
- Add POST /auth/logout endpoint that clears the cookie
- get_current_member falls back to rh_token cookie when no Authorization header
- WebSocket auth now accepts cookie (rh_token) or optional ?token= query param
- Frontend removes all localStorage JWT access; uses credentials:"include" on
  every fetch so the httpOnly cookie is sent automatically
- Replace clearToken() with logout() that calls the server logout endpoint
- Non-sensitive rh_session flag in localStorage used only for client-side routing

Rate limiting:
- Add slowapi>=0.1.9 dependency
- /auth/login limited to 10 req/min per IP
- /auth/register limited to 5 req/min per IP

Nginx security headers:
- Add X-Frame-Options, X-Content-Type-Options, Referrer-Policy,
  X-XSS-Protection, Permissions-Policy to all responses

SSE error leakage:
- songs.py nc-scan/stream no longer leaks str(exc) to clients

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:11:53 +02:00
Mistral Vibe
68da26588a security: fix auth, CORS, file upload, endpoint hardening + test fixes
- Add INTERNAL_SECRET shared-secret auth to /internal/nc-upload endpoint
- Add JWT token validation to WebSocket /ws/versions/{version_id}
- Fix NameError: band_slug → band.slug in internal.py
- Move inline imports to top of internal.py; add missing Member/NextcloudClient imports
- Remove ~15 debug print() statements from auth.py
- Replace Content-Type-only avatar check with extension whitelist + Pillow Image.verify()
- Sanitize exception details in versions.py (no more str(e) in 4xx/5xx responses)
- Restrict CORS allow_methods/allow_headers from "*" to explicit lists
- Add security headers middleware: X-Frame-Options, X-Content-Type-Options, Referrer-Policy
- Reduce JWT expiry from 7 days to 1 hour
- Add Pillow>=10.0 dependency; document INTERNAL_SECRET in .env.example
- Implement missing RedisJobQueue.dequeue() method (required by protocol)
- Fix 5 pre-existing unit test failures: settings env vars conftest, deferred Redis push,
  dequeue method, AsyncMock→MagicMock for sync scalar_one_or_none

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:02:56 +02:00
Mistral Vibe
efef818612 Merge branch 'feature/user-avatars'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 20:41:53 +02:00
Mistral Vibe
cd1d098ca4 fix: avatar stale state, nginx intercept, and dev tooling
Frontend (SettingsPage):
- Sync avatarUrl state via useEffect when me.avatar_url changes after
  background refetch, so profile section never shows stale avatar
- Invalidate ["comments"] after upload/generate/remove so SongPage
  comment avatars update immediately instead of waiting for staleTime
- Fix Remove button: was sending avatar_url: undefined which JSON.stringify
  drops entirely, so the server never cleared it; now sends ""

nginx:
- Change /api/ and /ws/ locations to use ^~ prefix so the static-asset
  regex rule (~* \.(png|svg|ico)$) cannot intercept API paths; PNG/SVG
  avatar uploads were returning 404 from nginx in production
- Merge nc-scan 300s timeout into ^~ /api/v1/bands/ block
- Add client_max_body_size 10m (default 1MB was silently rejecting
  uploads before they reached FastAPI)

Dev tooling:
- Add docker-compose.dev.yml for hot-reload development workflow
- Add Taskfile.yml with dev, test, lint, migrate, and shell tasks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 20:41:32 +02:00
Mistral Vibe
cd6fabb31c fix: correct avatar upload and DiceBear URL version
- Add api.upload() to client.ts that passes FormData without setting
  Content-Type, letting the browser set multipart/form-data with the
  correct boundary (was causing 422 on the upload endpoint)
- Use api.upload() instead of api.post() for avatar file upload
- Update DiceBear URLs from v6 to 9.x in both frontend and backend

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 20:17:21 +02:00
Mistral Vibe
da051be673 fix: proper TypeScript type assertion for error object
- Use type assertion to define error object structure
- Use optional chaining for safe property access
- Maintain all error handling functionality

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:58:29 +02:00
Mistral Vibe
e4beebc57e fix: add type guard for detail property in error data
- Check for 'detail' property before accessing it
- Maintain all error handling functionality
- Ensure TypeScript type safety

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:57:24 +02:00
Mistral Vibe
b66660ee30 fix: TypeScript type safety for error object properties
- Add proper type guards for error object properties
- Check for 'status' and 'data' properties before accessing
- Maintain all debugging functionality

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:56:41 +02:00
Mistral Vibe
a62297f2c4 fix: add comprehensive debugging and validation for avatar uploads
- Add detailed error extraction from API responses
- Validate file content is not empty before saving
- Verify file was actually saved to disk
- Check saved file size matches expectations
- Add extensive logging for debugging upload issues
- Improve error messages with specific details

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:55:20 +02:00
Mistral Vibe
b20b98a17a fix: improve error handling for avatar uploads
- Change invalid file type error from 400 to 422 for better frontend handling
- Add specific error message for 422 responses in frontend
- Improve error message clarity
- Better error classification and user guidance

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:49:39 +02:00
Mistral Vibe
48969e110a fix: remove incorrect headers parameter from API call
- Remove extra parameter that was causing TypeScript error
- Keep all other file size handling improvements

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:44:00 +02:00
Mistral Vibe
26f45009d4 fix: improve file size handling and error messages
- Reduce server-side limit to 5MB for upload endpoint
- Increase client-side resizing threshold to 4MB
- Add specific error handling for 413 responses
- Add more detailed logging for file sizes
- Improve user error messages

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:43:22 +02:00
Mistral Vibe
a63a1571ba feat: implement client-side image resizing for avatar uploads
- Add resizeImage function to SettingsPage
- Resize images larger than 2MB to max 800x800 pixels
- Convert to JPEG with 80% quality to reduce file size
- Add server-side validation for 10MB file size limit
- Maintain aspect ratio during resizing
- Log resizing details for debugging

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:40:33 +02:00
Mistral Vibe
675399836c fix: add comprehensive logging to debug avatar issues
- Add console.log statements to frontend avatar functions
- Add print statements to backend avatar endpoints
- Log file uploads, settings updates, and errors
- Track the complete flow from UI to backend

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:34:28 +02:00
Mistral Vibe
a17b4a7ec0 fix: TypeScript errors in avatar implementation
- Remove unnecessary headers parameter from API call
- Fix unused error parameter in onError handler
- Use undefined instead of null for avatar_url removal

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:28:41 +02:00
Mistral Vibe
b59eb584a6 fix: implement proper avatar upload and display
- Add file upload endpoint to auth router
- Mount static files for avatar serving
- Implement real file upload in frontend
- Add error handling and fallback for broken images
- Fix avatar persistence and state management
- Add loading states and proper error messages

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:27:35 +02:00
Mistral Vibe
184a288b7f fix: add avatar_url to updateSettings type definition
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:17:19 +02:00
Mistral Vibe
ccafcd38af feat: implement user avatars with DiceBear integration
- Add avatar_url field to MemberSettingsUpdate schema
- Create AvatarService for generating default avatars using DiceBear
- Update auth service to generate avatars on user registration
- Add avatar upload UI to settings page
- Update settings endpoint to handle avatar URL updates
- Display current avatar in settings with upload/generate options

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-03-30 19:15:24 +02:00
Mistral Vibe
3b8c4a0cb8 fix: comment waveform integration with timestamps and avatars
- Add author_avatar_url to API schema and frontend interface
- Capture current playhead timestamp when creating comments
- Display user avatars in waveform markers instead of placeholders
- Improve marker visibility with better styling (size, borders, shadows)
- Fix TypeScript type errors for nullable timestamps
- Add debug logging for troubleshooting

This implements the full comment waveform integration as requested:
- Comments are created with exact playhead timestamps
- Waveform markers show at correct positions with user avatars
- Clicking markers scrolls to corresponding comments
- Backward compatible with existing comments without timestamps
2026-03-30 19:06:40 +02:00
Mistral Vibe
86d4c8fad6 WIP: Comment waveform integration with todos and known issues 2026-03-29 22:56:19 +02:00
Mistral Vibe
b13e4505da Fix API health check and complete comment timestamp implementation 2026-03-29 22:32:30 +02:00
Mistral Vibe
a80c936537 Fix frontend errors and add timestamp support to comments 2026-03-29 22:10:21 +02:00
Mistral Vibe
a8aba72b3a WIP: Add timestamp to comments and fix frontend errors 2026-03-29 22:06:36 +02:00
Mistral Vibe
f7a07ba05e Update all files 2026-03-29 20:44:23 +02:00
Mistral Vibe
19a119ace2 Merge fix-play-behaviour into main
Implements proper play behaviour in track view:
- Version switching stops playback and resets UI state
- Playback must be started manually after version switch
- Added space key shortcut for play/pause toggle
- UI elements correctly reflect playback state

Fixes playback control issues after version switching
Enhances user experience with keyboard shortcuts
2026-03-29 20:42:14 +02:00
Mistral Vibe
e71c7fc3ad Fix play behaviour in track view
- Version switching now stops playback and resets UI state
- Playback must be started manually after version switch
- Added space key shortcut for play/pause toggle
- UI elements correctly reflect playback state

Fixes: Play/pause controls work correctly after version switching
Improves: User experience with keyboard shortcuts
2026-03-29 20:39:15 +02:00
Mistral Vibe
1179d9f063 WIP: Fix play behaviour in track view
- Added wasPlayingRef to preserve playback state across version changes
- Auto-play new waveform if previous version was playing
- Ensure play/pause buttons work correctly after version switch
- Added WebSocket proxy configuration for real-time features

Fixes: Version switching now preserves playback state
Todo: Test edge cases and finalize implementation
2026-03-29 20:37:25 +02:00
Mistral Vibe
02fd556372 feat: remove global Nextcloud config, enforce member-specific storage providers
- Remove global Nextcloud settings from config
- Make NextcloudClient require explicit credentials
- Update for_member() to return None when no credentials
- Modify services to accept optional storage client
- Update routers to pass member storage to services
- Add 403 responses when no storage provider configured
- Update internal endpoints to use member storage credentials

This change enforces that each member must configure their own
Nextcloud storage provider. If no provider is configured,
file operations will return 403 FORBIDDEN instead of falling
back to global placeholders.
2026-03-29 20:06:12 +02:00
Steffen Schuhmann
5e169342db docs: add ARCHITECTURE.md for agent/developer onboarding
Covers: service topology, directory layout, data model, full API surface,
scan/import pipeline, audio analysis flow, auth model, and key conventions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 16:08:23 +02:00
Steffen Schuhmann
1ea22d8b55 fix: each file in a dated session folder becomes its own song
Files directly in YYMMDD/ (e.g. 231015/take1.wav, 231015/take2.wav) all
shared the same nc_folder_path, so take2 was silently merged as a new
version of the take1 song instead of being a separate recording.

Fix: when a file's parent == the session folder, append the file stem
to make a unique virtual nc_folder_path per file. Files in subfolders
(e.g. 231015/groove/take1.wav) are unaffected — they still group by folder.

Applied in both nc_scan.py (manual scan) and internal.py (watcher uploads).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 15:47:10 +02:00
Steffen Schuhmann
8f86ecbeec fix: model_validate multiline call in get_session_detail (sessions.py:64)
sed one-liner only handled single-line calls; this one spanned 3 lines.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 15:35:55 +02:00
Steffen Schuhmann
7c643ff67b fix: replace model_validate(..., update=) with .model_copy(update=)
Pydantic v2 model_validate() does not accept an update kwarg; the correct
pattern is model_validate(obj).model_copy(update={...}).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 15:20:42 +02:00
Steffen Schuhmann
ececcb24f6 fix: remove unused NcScanResult interface in BandPage (TS error)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 15:14:18 +02:00
Steffen Schuhmann
7cad3e544a feat: incremental SSE scan, recursive NC traversal, custom folder support
- nc_scan.py: recursive collect_audio_files (fixes depth-1 bug); scan_band_folder
  yields ndjson events (progress/song/session/skipped/done) for streaming
- songs.py: replace old flat scan with scan_band_folder; add GET nc-scan/stream
  endpoint using _member_from_request so ?token= auth works for fetch-based SSE
- BandPage.tsx: scan button now consumes ndjson stream via fetch+ReadableStream;
  sessions/unattributed invalidated as each song/session event arrives
- session.py: add extract_session_folder() for YYMMDD path extraction
- rehearsal_session.py: get_or_create uses begin_nested() savepoint to handle races
- band.py: add get_by_nc_folder_prefix() for custom nc_folder_path band lookup
- internal.py: nc-upload falls back to prefix match when slug lookup fails
- event_loop.py: remove hardcoded bands/ guard; let internal API handle filtering

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 15:09:42 +02:00
Steffen Schuhmann
dc6dd9dcfd fix: scan visibility, NC folder validation, watcher logging
- nc-scan: detailed INFO logging of every path found, subfolder
  contents and skip reasons; 502 now includes the exact folder and
  error so user sees a real message instead of a blank result
- band creation: if nc_base_path is explicitly given, verify the
  folder exists in Nextcloud before saving — returns 422 with a
  clear message to the user; auto-generated paths still do MKCOL
- songs search: add ?unattributed=true to return songs with no
  session_id (files not in a YYMMDD folder)
- BandPage: show "Unattributed Recordings" section below sessions
  so scanned files without a dated folder always appear
- watcher event_loop: promote all per-activity log lines from DEBUG
  to INFO so they're visible in default Docker Compose log output;
  log normalized path and skip reason for every activity

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 14:11:07 +02:00