# Docker Volume JSON Storage Deployment Guide The Reading Tracker app now supports persistent JSON storage using Docker volumes, enabling multi-device access and data persistence across container restarts. ## Architecture Overview The application now consists of: - **Frontend**: React SPA (served by Express in production) - **Backend**: Express.js server with API endpoints - **Storage**: JSON files in Docker volume (`/app/data`) - **Fallback**: localStorage for offline/server unavailable scenarios ## Docker Deployment ### Quick Start with Docker Compose ```bash # Build and start the application docker-compose up -d # The app will be available at http://localhost:8080 ``` ### Manual Docker Commands ```bash # Build the image docker build -t reading-tracker:latest . # Run with persistent storage docker run -d \ --name reading-tracker \ -p 8080:80 \ -v reading-data:/app/data \ --restart unless-stopped \ reading-tracker:latest ``` ### For Coolify or Similar Platforms Use these settings: - **Port**: 80 (internal), map to your desired external port - **Volume Mount**: `/app/data` (for JSON storage) - **Health Check**: `GET /api/health` - **Environment Variables**: - `NODE_ENV=production` - `DATA_DIR=/app/data` (optional, defaults to `/app/data`) - `ALLOWED_ORIGINS=https://your-domain.com` (comma-separated for production CORS) ## API Endpoints - `GET /api/books` - Load all books from JSON storage - `POST /api/books` - Save books array to JSON storage - `GET /api/health` - Health check endpoint ## Data Storage Details ### File Location - **Production**: `/app/data/books.json` (Docker volume) - **Development**: `./data/books.json` (local directory) ### JSON Format ```json { "books": [ { "id": 1, "title": "Book Title", "author": "Author Name", "totalPages": 300, "currentPage": 150, "startDate": "2025-01-01", "targetDate": "2025-02-01", "readingHistory": { "2025-01-01": 10, "2025-01-02": 25 }, "createdAt": "2025-01-01T00:00:00.000Z" } ], "lastModified": "2025-08-17T21:46:52.274Z", "version": "1.0" } ``` ### Security Features - **Security Headers**: Helmet.js with CSP, anti-clickjacking, MIME protection - **Rate Limiting**: 100 req/15min general, 20 req/15min for saves - **Input Validation**: String limits, numeric bounds, date format validation - **CORS Protection**: Configurable origins for production - **Error Disclosure Prevention**: Generic error messages, sanitized logs - **Container Security**: Non-root user, minimal Alpine base image 📋 **See [SECURITY.md](./SECURITY.md) for complete security documentation** ## Fallback Behavior The app gracefully handles server unavailability: 1. **Load Priority**: Server JSON → localStorage fallback 2. **Save Priority**: Server JSON → localStorage backup 3. **Offline Mode**: Continues working with localStorage 4. **Auto-Recovery**: Syncs with server when available ## Development ### Local Development ```bash # Install dependencies npm install # Start both frontend and backend npm run dev # Frontend: http://localhost:5173 # Backend: http://localhost:3001 ``` ### Backend Only ```bash npm run server ``` ## Volume Management ### Backup Data ```bash # Create backup docker cp reading-tracker:/app/data/books.json ./backup-books.json # Restore backup docker cp ./backup-books.json reading-tracker:/app/data/books.json ``` ### View Volume Contents ```bash # List volume contents docker exec reading-tracker ls -la /app/data # View current data docker exec reading-tracker cat /app/data/books.json ``` ## Troubleshooting ### Health Check ```bash curl http://localhost:8080/api/health ``` ### Check Logs ```bash docker logs reading-tracker ``` ### Volume Issues ```bash # Verify volume mount docker inspect reading-tracker | grep -A 10 "Mounts" # Recreate volume if needed docker volume rm reading-data docker-compose up -d ``` ## Migration from localStorage When first deploying, the app will: 1. Try to load from server (empty initially) 2. Fall back to localStorage if available 3. Save localStorage data to server on first interaction 4. Continue using server storage for all subsequent operations No manual migration needed - the fallback system handles it automatically!