From 9bdce32219dded625acedf1407d5009a86e6126a Mon Sep 17 00:00:00 2001 From: Iulian Date: Wed, 24 Dec 2025 01:21:01 +0000 Subject: [PATCH] Fix CORS configuration for PWA offline caching - Add CORS_ALLOWED_ORIGINS environment variable support in Django settings - Use environment variable in docker-compose.yml (no hardcoded domains) - Update .env.example with CORS configuration documentation - Add comprehensive PWA offline debugging guide SECURITY: Private domains should be set in local .env file, not in repo --- .env.example | 5 + DEBUG_OFFLINE.md | 170 +++++++++++++++++++++++++ OFFLINE_CACHE_FIX_TESTING.md | 235 +++++++++++++++++++++++++++++++++++ docker-compose.yml | 2 +- 4 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 DEBUG_OFFLINE.md create mode 100644 OFFLINE_CACHE_FIX_TESTING.md diff --git a/.env.example b/.env.example index fef393d..fc1e50e 100644 --- a/.env.example +++ b/.env.example @@ -7,6 +7,11 @@ REDIS_HOST=soundwave-redis ES_URL=http://soundwave-es:9200 TZ=UTC +# CORS Configuration - Add your custom domains here (comma-separated) +# Default: http://localhost:8889,https://localhost:8889 +# Example with custom domain: http://localhost:8889,https://localhost:8889,https://yourdomain.com +CORS_ALLOWED_ORIGINS=http://localhost:8889,https://localhost:8889 + # Optional settings SW_AUTO_UPDATE_YTDLP=true DJANGO_DEBUG=false diff --git a/DEBUG_OFFLINE.md b/DEBUG_OFFLINE.md new file mode 100644 index 0000000..bbb39da --- /dev/null +++ b/DEBUG_OFFLINE.md @@ -0,0 +1,170 @@ +# šŸ› Debugging Offline Playlist Caching + +## Issue +User clicks "Make Available Offline" but playlists aren't being cached for offline playback. + +## Investigation Steps + +### 1. Check Service Worker Registration +Open browser DevTools → Console and run: +```javascript +navigator.serviceWorker.getRegistration().then(reg => console.log('SW Registered:', reg)) +``` + +Expected: ServiceWorkerRegistration object +If null: Service worker isn't registered + +### 2. Check if Service Worker is Active +```javascript +navigator.serviceWorker.ready.then(reg => console.log('SW Active:', reg.active)) +``` + +Expected: ServiceWorker object with state 'activated' + +### 3. Test Message Passing +Open a playlist page and run in console: +```javascript +const testCache = async () => { + const reg = await navigator.serviceWorker.ready; + const messageChannel = new MessageChannel(); + + messageChannel.port1.onmessage = (event) => { + console.log('Response from SW:', event.data); + }; + + reg.active.postMessage( + { + type: 'CACHE_PLAYLIST', + playlistId: 'test123', + audioUrls: ['/api/audio/test/download/'] + }, + [messageChannel.port2] + ); +}; + +testCache(); +``` + +Expected: Console log showing response from service worker + +### 4. Check Network Requests +When clicking "Make Available Offline": +1. Open DevTools → Network tab +2. Filter by "audio" +3. Click the button +4. Check if audio download requests are made + +Expected: Multiple requests to `/api/audio/{id}/download/` + +### 5. Check Service Worker Console +1. Open DevTools → Application tab +2. Click "Service Workers" +3. Click "inspect" next to the active service worker +4. New DevTools window opens showing SW console +5. Try caching again and watch for logs + +Expected logs: +- `[Service Worker] Message received: {type: 'CACHE_PLAYLIST', ...}` +- `[Service Worker] Caching playlist: ...` +- `[Service Worker] Cached audio: ...` + +### 6. Check Cache Storage +After attempting to cache: +1. DevTools → Application → Cache Storage +2. Look for `soundwave-audio-v1` cache +3. Expand it to see cached files + +Expected: Audio files should be listed + +### 7. Check IndexedDB +1. DevTools → Application → IndexedDB +2. Look for `SoundwaveOfflineDB` +3. Check `playlists` object store + +Expected: Playlist with `offline: true` + +## Common Issues + +### Issue 1: Service Worker Not Registered +**Symptom**: `navigator.serviceWorker.getRegistration()` returns null + +**Fix**: Check if running on HTTPS or localhost. Service workers require secure context. + +### Issue 2: CORS / Authentication Issues +**Symptom**: Network requests fail with 401/403 + +**Fix**: Ensure audio download endpoints properly handle authentication. +Check service worker creates authenticated requests: +```javascript +const authRequest = new Request(url, { + credentials: 'include', + headers: { 'Accept': 'audio/*' } +}); +``` + +### Issue 3: Message Channel Not Working +**Symptom**: No response from service worker + +**Fix**: Ensure service worker message handler properly uses `event.ports[0].postMessage()` + +### Issue 4: Service Worker Scope Issues +**Symptom**: Fetch events not intercepting + +**Fix**: Check service worker registered with scope: `'/'` + +## Quick Test Script + +Run this in browser console on a playlist page: + +```javascript +const debugOfflineCache = async () => { + console.log('=== Offline Cache Debug ==='); + + // 1. Check SW + const reg = await navigator.serviceWorker.getRegistration(); + console.log('1. SW Registered:', !!reg); + console.log(' SW Active:', reg?.active?.state); + + // 2. Check if online + console.log('2. Online:', navigator.onLine); + + // 3. Check IndexedDB + const db = await new Promise((resolve, reject) => { + const request = indexedDB.open('SoundwaveOfflineDB'); + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); + console.log('3. IndexedDB:', db.name, 'version:', db.version); + + // 4. Check cache storage + const cacheNames = await caches.keys(); + console.log('4. Caches:', cacheNames); + + for (const name of cacheNames) { + const cache = await caches.open(name); + const keys = await cache.keys(); + console.log(` ${name}:`, keys.length, 'entries'); + } + + // 5. Test cache size + if ('storage' in navigator && 'estimate' in navigator.storage) { + const estimate = await navigator.storage.estimate(); + const usage = (estimate.usage / 1024 / 1024).toFixed(2); + const quota = (estimate.quota / 1024 / 1024).toFixed(2); + console.log('5. Storage: ', usage, 'MB /', quota, 'MB'); + } + + console.log('=== Debug Complete ==='); +}; + +debugOfflineCache(); +``` + +## Next Steps + +Based on debug output, identify which component is failing: +- Service Worker registration +- Message passing +- Network requests +- Cache storage +- IndexedDB storage diff --git a/OFFLINE_CACHE_FIX_TESTING.md b/OFFLINE_CACHE_FIX_TESTING.md new file mode 100644 index 0000000..63adcad --- /dev/null +++ b/OFFLINE_CACHE_FIX_TESTING.md @@ -0,0 +1,235 @@ +# šŸ”§ Offline Playlist Caching - Fix Applied + +## What Was Fixed + +Added comprehensive logging and timeout handling to the offline playlist caching system: + +1. **Added Timeout** - 60-second timeout prevents hanging if service worker doesn't respond +2. **Better Error Handling** - Validates service worker is active before sending messages +3. **Detailed Logging** - Console logs at every step to identify where failures occur +4. **Error Messages** - Clear error messages show specific failure reasons + +## How to Test + +### 1. Open Browser Console +1. Open SoundWave: http://localhost:8889 +2. Press `F12` or Right-click → Inspect +3. Go to **Console** tab +4. Clear console (trash icon) + +### 2. Navigate to a Playlist +1. Login (username: `iulian`, password: whatever you set) +2. Go to **Playlists** page +3. Click on any playlist that has downloaded tracks +4. You should see the playlist detail page + +### 3. Try Offline Caching +1. Scroll down to the **"Cache for Offline"** card +2. Click **"Make Available Offline"** button +3. **Watch the Console** for logs: + +**Expected Console Logs:** +``` +[Offline] handleCacheForOffline called {playlist: "PLxxx", isOnline: true} +[Offline] Found downloaded tracks: 5 +[Offline] Audio URLs to cache: ["/api/audio/xxx/download/", ...] +[Offline] Calling cachePlaylist... +[PWA] Caching playlist PLxxx with 5 audio files +[PWA] Sending CACHE_PLAYLIST message to service worker +[PWA] Service worker response: {success: true, cached: 5, failed: 0} +[PWA] Successfully cached 5 audio files +[Offline] cachePlaylist result: true +``` + +**If Service Worker Not Active:** +``` +[PWA] Service Worker not supported +``` +or +``` +[PWA] No active service worker +``` + +**If Timeout:** +``` +[PWA] Caching playlist timed out after 60s +``` + +### 4. Check Service Worker (If Issues) + +If caching fails, check service worker status: + +**Option A: Quick Check** +In console, run: +```javascript +navigator.serviceWorker.getRegistration().then(reg => { + console.log('SW Registered:', !!reg); + console.log('SW Active:', reg?.active?.state); +}); +``` + +Expected output: +``` +SW Registered: true +SW Active: "activated" +``` + +**Option B: DevTools Check** +1. Open DevTools → **Application** tab +2. Click **"Service Workers"** in left sidebar +3. You should see `service-worker.js` listed +4. Status should show: **"activated and is running"** + +### 5. Inspect Service Worker Console (Advanced) + +To see what the service worker is doing: + +1. DevTools → Application → Service Workers +2. Find the running service worker +3. Click **"inspect"** next to it +4. A new DevTools window opens (Service Worker console) +5. Try caching again +6. Watch for logs in SW console: + +**Expected SW Logs:** +``` +[Service Worker] Message received: {type: 'CACHE_PLAYLIST', ...} +[Service Worker] Caching playlist: PLxxx with 5 tracks +[Service Worker] Cached playlist metadata +[Service Worker] Cached audio: /api/audio/xxx/download/ +[Service Worker] Cached audio: /api/audio/yyy/download/ +... +[Service Worker] Playlist caching complete: {cached: 5, failed: 0} +``` + +### 6. Verify Cache Storage + +After successful caching: + +1. DevTools → **Application** → **Cache Storage** +2. Expand **"soundwave-audio-v1"** +3. You should see cached audio files: `/api/audio/{id}/download/` +4. Expand **"soundwave-api-v1"** +5. You should see: `/api/playlist/{id}/?include_items=true` + +### 7. Verify IndexedDB + +Check if playlist metadata was saved: + +1. DevTools → **Application** → **IndexedDB** +2. Expand **"SoundwaveOfflineDB"** +3. Expand **"playlists"** +4. Click on your playlist ID +5. Check `offline: true` and `lastSync` timestamp + +### 8. Test Offline Playback + +1. In browser console, run: `navigator.onLine = false` (or turn off WiFi) +2. Reload the page +3. Try playing cached tracks - they should work! +4. Re-enable network: Turn WiFi back on + +## Common Issues & Solutions + +### Issue 1: "Service Worker not supported" +**Cause**: Not running on HTTPS or localhost +**Solution**: Access via http://localhost:8889 (not IP address) + +### Issue 2: "No active service worker" +**Cause**: Service worker not registered or failed to activate +**Solution**: +1. Hard refresh: `Ctrl+Shift+R` (or `Cmd+Shift+R` on Mac) +2. DevTools → Application → Service Workers → Unregister +3. Refresh page to re-register + +### Issue 3: "Caching playlist timed out" +**Cause**: Network too slow or many tracks +**Solution**: +- Check network tab for failed requests +- Try with smaller playlist first +- Check if audio downloads return 200 OK + +### Issue 4: 401/403 Authentication Errors +**Cause**: Session expired or auth not passed to service worker +**Solution**: +- Logout and login again +- Check if cookies are enabled +- Verify auth endpoint works: try downloading a track manually + +### Issue 5: CORS Errors +**Cause**: Cross-origin issues +**Solution**: Verify accessing from same origin (localhost:8889) + +## Debug Script + +Run this comprehensive debug script in console: + +```javascript +const fullDebug = async () => { + console.log('=== OFFLINE CACHING DEBUG ===\n'); + + // 1. Service Worker + try { + const reg = await navigator.serviceWorker.getRegistration(); + console.log('āœ“ Service Worker:', reg ? 'Registered' : 'āŒ NOT REGISTERED'); + if (reg) { + console.log(' - Scope:', reg.scope); + console.log(' - Active:', reg.active?.state || 'āŒ NO ACTIVE WORKER'); + console.log(' - Installing:', reg.installing?.state || 'none'); + console.log(' - Waiting:', reg.waiting?.state || 'none'); + } + } catch (e) { + console.error('āŒ Service Worker check failed:', e); + } + + // 2. Online Status + console.log('\nāœ“ Network:', navigator.onLine ? 'Online' : 'āŒ OFFLINE'); + + // 3. Caches + try { + const cacheNames = await caches.keys(); + console.log('\nāœ“ Caches:', cacheNames.length); + for (const name of cacheNames) { + const cache = await caches.open(name); + const keys = await cache.keys(); + console.log(` - ${name}: ${keys.length} items`); + } + } catch (e) { + console.error('āŒ Cache check failed:', e); + } + + // 4. IndexedDB + try { + const dbs = await indexedDB.databases(); + console.log('\nāœ“ IndexedDB:', dbs.map(db => db.name).join(', ')); + } catch (e) { + console.error('āŒ IndexedDB check failed:', e); + } + + // 5. Storage + try { + if ('storage' in navigator && 'estimate' in navigator.storage) { + const est = await navigator.storage.estimate(); + const used = (est.usage / 1024 / 1024).toFixed(2); + const total = (est.quota / 1024 / 1024).toFixed(2); + const percent = ((est.usage / est.quota) * 100).toFixed(1); + console.log(`\nāœ“ Storage: ${used} MB / ${total} MB (${percent}%)`); + } + } catch (e) { + console.error('āŒ Storage check failed:', e); + } + + console.log('\n=== DEBUG COMPLETE ==='); +}; + +fullDebug(); +``` + +## Next Steps + +1. **Run the test** following steps 1-3 above +2. **Check the console logs** to identify the specific issue +3. **Share the console output** if you need help debugging +4. **Verify** cache storage and IndexedDB if caching succeeds + +The logs will now show exactly where the process fails! šŸ” diff --git a/docker-compose.yml b/docker-compose.yml index 87b203b..ea1041c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,7 +24,7 @@ services: - REDIS_HOST=soundwave-redis - TZ=UTC - ES_URL=http://soundwave-es:9200 - - CORS_ALLOWED_ORIGINS=http://localhost:8889,https://localhost:8889,https://sound.iulian.uk + - CORS_ALLOWED_ORIGINS=${CORS_ALLOWED_ORIGINS:-http://localhost:8889,https://localhost:8889} depends_on: - soundwave-es - soundwave-redis