- Full PWA support with offline capabilities - Comprehensive search across songs, playlists, and channels - Offline playlist manager with download tracking - Pre-built frontend for zero-build deployment - Docker-based deployment with docker compose - Material-UI dark theme interface - YouTube audio download and management - Multi-user authentication support
8.4 KiB
Data Persistence & PWA Offline Fix
🎯 Issues Fixed
1. Database Persistence ✅
Problem: Downloaded playlists were lost on container rebuild because SQLite database was not persisted.
Solution:
- Created
/app/datavolume mount in Docker - Updated Django settings to store
db.sqlite3in persistent/app/datadirectory - Added
data/directory with proper.gitignore
2. Route Conflicts ✅
Problem: Playlist download routes conflicted with main playlist routes (both at root path '')
Solution:
- Moved download routes to
downloads/prefix - Proper route ordering in
backend/playlist/urls.py - API endpoints now:
/api/playlist/downloads/instead of/api/playlist/
3. PWA Offline Playlist Caching ✅
Problem: No dedicated offline caching strategy for playlists
Solution:
- Added
cachePlaylist()andremovePlaylistCache()to PWA Manager - Enhanced Service Worker with playlist-specific cache handlers
- Added playlist methods to IndexedDB storage:
savePlaylist()getOfflinePlaylists()updatePlaylistSyncStatus()
- Updated PWA Context to expose playlist caching functions
4. Security Audit ✅
Verified:
- ✅ All sensitive endpoints require authentication
- ✅ User isolation with
IsOwnerOrAdminpermission - ✅ Admin-only routes properly protected
- ✅ CORS and CSRF configured correctly
- ✅ Token authentication working
📁 Files Modified
Backend
docker-compose.yml- Addeddataandstaticfilesvolumesbackend/config/settings.py- Database path now/app/data/db.sqlite3backend/playlist/urls.py- Fixed route conflicts
Frontend (PWA)
frontend/src/utils/offlineStorage.ts- Added playlist offline methodsfrontend/src/utils/pwa.ts- AddedcachePlaylist()andremovePlaylistCache()frontend/src/context/PWAContext.tsx- Exposed new playlist functionsfrontend/public/service-worker.js- Added playlist cache handlers
Infrastructure
data/.gitignore- Created to exclude database from git
🚀 Migration Steps
For Existing Deployments
-
Stop containers:
docker-compose down -
Create data directory (if not exists):
mkdir -p data -
Migrate existing database (if you have one):
# If you have an existing db.sqlite3 in backend/ mv backend/db.sqlite3 data/db.sqlite3 -
Rebuild and restart:
docker-compose build docker-compose up -d -
Verify persistence:
# Check database exists ls -lh data/db.sqlite3 # Check it persists after rebuild docker-compose down docker-compose up -d ls -lh data/db.sqlite3 # Should still exist
🎨 PWA Offline Playlist Usage
In Your Components
import { usePWA } from '../context/PWAContext';
import { offlineStorage } from '../utils/offlineStorage';
function PlaylistComponent() {
const { cachePlaylist, removePlaylistCache, isOnline } = usePWA();
// Download playlist for offline use
const downloadPlaylist = async (playlist) => {
// 1. Cache audio files via Service Worker
const audioUrls = playlist.items.map(item => item.audio_url);
const cached = await cachePlaylist(playlist.id, audioUrls);
// 2. Save metadata to IndexedDB
if (cached) {
await offlineStorage.savePlaylist({
id: playlist.id,
title: playlist.title,
items: playlist.items,
offline: true,
});
}
};
// Remove offline playlist
const removeOfflinePlaylist = async (playlist) => {
const audioUrls = playlist.items.map(item => item.audio_url);
await removePlaylistCache(playlist.id, audioUrls);
await offlineStorage.removePlaylist(playlist.id);
};
// Get offline playlists
const loadOfflinePlaylists = async () => {
const playlists = await offlineStorage.getOfflinePlaylists();
return playlists;
};
}
📊 Data Persistence Structure
soundwave/
├── audio/ # Persistent: Downloaded audio files
├── cache/ # Persistent: Application cache
├── data/ # ✨ NEW: Persistent database storage
│ ├── db.sqlite3 # Main database (persists between rebuilds)
│ └── .gitignore # Excludes database from git
├── es/ # Persistent: Elasticsearch data
├── redis/ # Persistent: Redis data
└── backend/
└── staticfiles/ # Persistent: Collected static files
🔒 Security Verification
All endpoints verified for proper authentication and authorization:
Public Endpoints (No Auth Required)
/api/user/login/- User login/api/user/register/- User registration
Authenticated Endpoints
/api/playlist/*- User playlists (owner isolation)/api/playlist/downloads/*- Download management (owner isolation)/api/audio/*- Audio files (user-scoped)/api/channel/*- Channels (admin write, all read)
Admin-Only Endpoints
/api/download/*- Download queue management/api/task/*- Task management/api/appsettings/*- System settings/admin/*- Django admin
Permission Classes Used
IsAuthenticated- Must be logged inIsOwnerOrAdmin- Owner or admin accessAdminOnly- Admin/superuser onlyAdminWriteOnly- Admin write, all read
🧪 Testing Checklist
- Database persists after
docker-compose down && docker-compose up - Downloaded playlists remain after container rebuild
- Audio files persist in
/audiovolume - Static files persist in
/staticfilesvolume - PWA offline playlist caching works
- Route conflicts resolved
- Security permissions verified
- Multi-user isolation working
- Full end-to-end test with rebuild
🎯 API Endpoint Changes
Before
/api/playlist/ # List/Create playlists
/api/playlist/<id>/ # Playlist detail
/api/playlist/ # ❌ CONFLICT: Downloads viewset
After
/api/playlist/ # List/Create playlists
/api/playlist/<id>/ # Playlist detail
/api/playlist/downloads/ # ✅ Downloads viewset (no conflict)
/api/playlist/downloads/<id>/ # Download detail
/api/playlist/downloads/active/ # Active downloads
/api/playlist/downloads/completed/# Completed downloads
💡 Best Practices
-
Always use volumes for persistent data
- Database files
- User uploads
- Application cache
- Static files
-
Separate data from code
- Code in container (rebuilt)
- Data in volumes (persisted)
-
PWA offline strategy
- Cache API responses for metadata
- Cache audio files for playback
- Store state in IndexedDB
- Sync when online
-
Security layers
- Authentication (token-based)
- Authorization (permission classes)
- User isolation (owner field checks)
- Admin protection (admin-only views)
📝 Environment Variables
Optional configuration in .env or docker-compose:
# Data directory (default: /app/data)
DATA_DIR=/app/data
# Media directory (default: /app/audio)
MEDIA_ROOT=/app/audio
🔄 Future Enhancements
-
Database Backup
- Add automated SQLite backup script
- Volume snapshot strategy
-
Cache Management
- PWA cache size limits
- Auto-cleanup old cached playlists
-
Sync Strategy
- Background sync for offline changes
- Conflict resolution
-
Analytics
- Track offline usage
- Cache hit/miss ratios
❓ Troubleshooting
Database not persisting
# Check volume mount
docker inspect soundwave | grep -A 5 Mounts
# Verify data directory
docker exec soundwave ls -lh /app/data/
# Check database location
docker exec soundwave python manage.py shell -c "from django.conf import settings; print(settings.DATABASES['default']['NAME'])"
PWA cache not working
# Check service worker registration
# Open browser DevTools -> Application -> Service Workers
# Clear all caches
# DevTools -> Application -> Storage -> Clear site data
# Re-register service worker
# Navigate to app and check console
Route conflicts
# Test endpoints
curl http://localhost:8889/api/playlist/
curl http://localhost:8889/api/playlist/downloads/
🎉 Result
✅ Playlists now persist between container rebuilds ✅ PWA offline support for playlists ✅ No route conflicts ✅ Security verified ✅ All users (admin & managed) working