From 7c7905268115a386d418e786fcd502966a046793 Mon Sep 17 00:00:00 2001 From: paul Date: Thu, 24 Jul 2025 20:54:50 +0200 Subject: [PATCH] refactor: consolidate deployment documentation and cleanup repository - Merge all deployment docs into single comprehensive DEPLOYMENT_GUIDE.md - Add instructions for non-nginx deployment options - Reference utility scripts in deployment guide - Remove orphaned migrations folder at root level - Remove redundant deployment documentation files - Keep all utility scripts in scripts/ folder - Update CLAUDE.md to reference new deployment guide This provides a single source of truth for all deployment scenarios. --- .gitattributes-github | 12 - .gitea/workflows/mirror-to-github.yml | 22 +- .github-mirror-exclude | 24 - .gitignore | 9 +- CLAUDE.md | 13 +- DEPLOYMENT.md | 220 ------- DEPLOYMENT_GUIDE.md | 761 +++++++++++++++++++++++ PRODUCTION_DEPLOYMENT_GUIDE.md | 378 ----------- migrations/005_add_watermark_settings.js | 61 -- test-maintenance.sh | 19 - 10 files changed, 779 insertions(+), 740 deletions(-) delete mode 100644 .gitattributes-github delete mode 100644 .github-mirror-exclude delete mode 100644 DEPLOYMENT.md create mode 100644 DEPLOYMENT_GUIDE.md delete mode 100644 PRODUCTION_DEPLOYMENT_GUIDE.md delete mode 100644 migrations/005_add_watermark_settings.js delete mode 100755 test-maintenance.sh diff --git a/.gitattributes-github b/.gitattributes-github deleted file mode 100644 index 9da591d..0000000 --- a/.gitattributes-github +++ /dev/null @@ -1,12 +0,0 @@ -# Files to exclude from GitHub mirror -.env* export-ignore -docker-compose.prod.yml export-ignore -.claudedocs/ export-ignore -backend/data/ export-ignore -backend/storage/ export-ignore -backend/.env* export-ignore -frontend/.env* export-ignore -secrets/ export-ignore -*.key export-ignore -*.pem export-ignore -.gitea/ export-ignore \ No newline at end of file diff --git a/.gitea/workflows/mirror-to-github.yml b/.gitea/workflows/mirror-to-github.yml index d77cec4..0eadc20 100644 --- a/.gitea/workflows/mirror-to-github.yml +++ b/.gitea/workflows/mirror-to-github.yml @@ -28,30 +28,12 @@ jobs: # Remove sensitive files/directories if they exist echo "Removing sensitive files..." - rm -rf .env || true - rm -rf backend/.env* || true - rm -rf frontend/.env* || true - rm -rf docker-compose.prod.yml || true - rm -rf .claudedocs/ || true - rm -rf backend/data/ || true - rm -rf backend/storage/ || true rm -rf .gitea/ || true - rm -rf scripts/install-gitea-runner.sh || true + rm -rf scripts/ || true rm -rf .drone* || true - rm -rf .github-mirror-exclude || true - rm -rf .gitattributes-github || true rm -rf photo-sharing-prd.md || true rm -rf CLAUDE.md || true - rm -rf PRODUCTION_DEPLOYMENT_GUIDE.md || true - rm -rf logs/ || true - rm -rf frontend/.claudedocs/ || true - rm -rf test-maintenance.sh || true - rm -rf storage/ || true - rm -rf clean-git-history.sh || true - rm -rf frontend/.swarm/ || true - rm -rf backend/.hive-mind/ || true - rm -rf data/ || true - rm -rf certbot/ || true + echo "Sensitive files removal completed" diff --git a/.github-mirror-exclude b/.github-mirror-exclude deleted file mode 100644 index dbc08bc..0000000 --- a/.github-mirror-exclude +++ /dev/null @@ -1,24 +0,0 @@ -# Exclude patterns for GitHub mirror -.env -.env.* -.env* -docker-compose.prod.yml -docker-compose.traefik.yml -.claudedocs/ -backend/data/ -backend/storage/ -backend/.env* -frontend/.env* -secrets/ -*.key -*.pem -.gitea/ -node_modules/ -dist/ -build/ -*.log -.DS_Store -deploy/ -certbot/ -nginx/ -photo-sharing-prd.md \ No newline at end of file diff --git a/.gitignore b/.gitignore index 22bdbc6..0ac1706 100644 --- a/.gitignore +++ b/.gitignore @@ -59,4 +59,11 @@ test-archiver/ !data/.gitkeep !logs/.gitkeep -PRODUCTION_DEPLOYMENT_GUIDE.md \ No newline at end of file +# development files +backend/.swarm/ +.claudedocs/ +backend/data/ +logs/ +storage/ +data/ +certbot/ diff --git a/CLAUDE.md b/CLAUDE.md index ae193dd..0a185b5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,11 +33,14 @@ npm test -- path/to/test.test.js npm test -- --testNamePattern="test name" ``` -### Production -```bash -docker-compose -f docker-compose.prod.yml up -d # Production deployment -pm2 start ecosystem.config.js # Alternative: PM2 deployment -``` +### Production Deployment +See [DEPLOYMENT_GUIDE.md](./DEPLOYMENT_GUIDE.md) for comprehensive deployment instructions including: +- Docker Compose deployment +- PM2 deployment +- Manual installation +- Non-nginx deployment options +- SSL/HTTPS setup +- Troubleshooting guide **⚠️ CRITICAL PRODUCTION NOTICE:** - Production runs on a SEPARATE SERVER - never assume local changes affect production diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md deleted file mode 100644 index 61ace45..0000000 --- a/DEPLOYMENT.md +++ /dev/null @@ -1,220 +0,0 @@ -# 🚀 PicPeak Deployment Guide - -This guide will help you deploy PicPeak in production. The entire process takes about 10-15 minutes. - -## 📋 Prerequisites - -- A server with Docker and Docker Compose installed -- A domain name (for SSL certificates) -- SMTP credentials for sending emails -- Basic command line knowledge - -## 🏃 Quick Deploy (Recommended) - -### 1. Clone and Configure - -```bash -# Clone the repository -git clone https://github.com/the-luap/picpeak.git -cd picpeak - -# Copy environment template -cp .env.production.example .env - -# Generate a secure JWT secret -echo "JWT_SECRET=$(openssl rand -base64 32)" >> .env - -# Edit configuration -nano .env -``` - -### 2. Required Environment Variables - -Edit your `.env` file with these essential settings: - -```env -# Application URLs -FRONTEND_URL=https://your-domain.com -BACKEND_URL=https://your-domain.com - -# Email Configuration (Required for notifications) -SMTP_HOST=smtp.gmail.com -SMTP_PORT=587 -SMTP_USER=your-email@gmail.com -SMTP_PASS=your-app-password -SMTP_FROM=your-email@gmail.com - -# Admin Configuration -ADMIN_EMAIL=admin@your-domain.com -ADMIN_PASSWORD=your-secure-password - -# Database (PostgreSQL for production) -DATABASE_CLIENT=pg -DB_HOST=postgres -DB_NAME=picpeak -DB_USER=picpeak -DB_PASSWORD=secure-db-password -``` - -### 3. Deploy with Docker Compose - -```bash -# Start all services -docker-compose -f docker-compose.prod.yml up -d - -# Check logs -docker-compose logs -f - -# Access your site at https://your-domain.com -``` - -## 🔧 Configuration Options - -### Storage Settings - -```env -# Storage paths (default: ./storage) -STORAGE_PATH=./storage -ARCHIVE_PATH=./storage/archives - -# Gallery expiration (days) -DEFAULT_EXPIRATION_DAYS=30 -WARNING_DAYS_BEFORE_EXPIRY=7 -``` - -### Security Settings - -```env -# Session timeout (minutes) -SESSION_TIMEOUT=60 - -# Rate limiting -RATE_LIMIT_WINDOW_MS=900000 # 15 minutes -RATE_LIMIT_MAX_REQUESTS=100 -``` - -### Analytics (Optional) - -```env -# Umami Analytics -VITE_UMAMI_URL=https://analytics.your-domain.com -VITE_UMAMI_WEBSITE_ID=your-website-id -``` - -## 🔒 SSL/TLS Setup - -The production Docker Compose includes automatic SSL via Let's Encrypt: - -1. **Ensure your domain points to your server** -2. **Update nginx configuration**: - ```bash - nano nginx/nginx.conf - # Replace your-domain.com with your actual domain - ``` -3. **Start services** - Certbot will automatically obtain certificates - -## 📁 Directory Structure - -After deployment, your directory structure will be: - -``` -picpeak/ -├── backend/ # API server -├── frontend/ # React app -├── storage/ # Photo storage -│ ├── events/ # Active galleries -│ │ ├── active/ # Current photos -│ │ └── archived/ # Expired galleries -│ ├── thumbnails/ # Generated thumbnails -│ └── uploads/ # User uploads -├── data/ # Database files -└── logs/ # Application logs -``` - -## 🔄 Maintenance - -### Backup - -```bash -# Backup database and photos -./scripts/backup.sh - -# Backups are stored in ./backups/ -``` - -### Update - -```bash -# Pull latest changes -git pull - -# Rebuild and restart -docker-compose -f docker-compose.prod.yml up -d --build -``` - -### Logs - -```bash -# View all logs -docker-compose logs - -# View specific service -docker-compose logs backend -docker-compose logs frontend -``` - -## 🚨 Troubleshooting - -### Common Issues - -**Photos not appearing:** -- Check storage permissions: `chmod -R 755 storage/` -- Verify file watcher is running: `docker-compose logs backend | grep watcher` - -**Email not sending:** -- Test SMTP settings: Admin Panel → Settings → Email → Send Test -- Check email queue: Admin Panel → System → Email Queue - -**Can't access admin panel:** -- Default login: Use email/password from `.env` -- Reset password: `docker exec picpeak-backend npm run reset-admin` - -### Health Check - -```bash -# Check service status -docker-compose ps - -# Test backend API -curl https://your-domain.com/api/health - -# Check disk space -df -h storage/ -``` - -## 🐳 Alternative Deployment Methods - -### Using Docker Swarm - -For high availability deployments, see [Docker Swarm Setup](deploy/README.md). - -### Manual Installation - -If you prefer not to use Docker: - -1. Install Node.js 18+ -2. Install PostgreSQL -3. Clone repository -4. Install dependencies: `npm install` in both `/backend` and `/frontend` -5. Build frontend: `cd frontend && npm run build` -6. Start services with PM2 - -## 📞 Support - -- 📘 [Documentation](https://github.com/the-luap/picpeak) -- 🐛 [Report Issues](https://github.com/the-luap/picpeak/issues) -- 💬 [Discussions](https://github.com/the-luap/picpeak/discussions) - ---- - -**Need help?** Open an issue on GitHub and we'll assist you! \ No newline at end of file diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md new file mode 100644 index 0000000..091092b --- /dev/null +++ b/DEPLOYMENT_GUIDE.md @@ -0,0 +1,761 @@ +# 🚀 PicPeak Complete Deployment Guide + +This comprehensive guide covers all deployment methods for PicPeak, including Docker, PM2, manual installation, and deployment without a reverse proxy. + +## 📋 Table of Contents + +- [Prerequisites](#prerequisites) +- [Security Requirements](#security-requirements) +- [Quick Start (Docker)](#quick-start-docker) +- [Deployment Methods](#deployment-methods) + - [Method 1: Docker Compose (Recommended)](#method-1-docker-compose-recommended) + - [Method 2: PM2 (Node.js Process Manager)](#method-2-pm2-nodejs-process-manager) + - [Method 3: Manual Installation](#method-3-manual-installation) + - [Method 4: Without Nginx (Direct Access)](#method-4-without-nginx-direct-access) +- [Environment Configuration](#environment-configuration) +- [Admin Setup](#admin-setup) +- [SSL/HTTPS Configuration](#sslhttps-configuration) +- [Maintenance & Operations](#maintenance--operations) +- [Troubleshooting](#troubleshooting) +- [Security Checklist](#security-checklist) + +## Prerequisites + +### Basic Requirements +- Linux server (Ubuntu 20.04+ or similar) +- Domain name (for SSL certificates) +- SMTP credentials for email notifications +- Basic command line knowledge + +### Software Requirements (varies by method) +- **Docker method**: Docker and Docker Compose +- **PM2 method**: Node.js 18+, PostgreSQL 14+ +- **Manual method**: Node.js 18+, PostgreSQL 14+, nginx (optional) + +## 🔐 Security Requirements + +### Critical: JWT Secret Setup + +**NEVER use the default JWT secret in production!** The application will refuse to start if JWT_SECRET is not properly configured. + +Generate a secure JWT secret: +```bash +# Generate a 64-character secret +openssl rand -base64 32 + +# Or for even more security (recommended) +openssl rand -base64 64 + +# Or use the included script +./scripts/generate-jwt-secret.sh +``` + +### Critical: Database Password + +Generate a strong database password: +```bash +openssl rand -base64 24 +``` + +## 🚀 Quick Start (Docker) + +The fastest way to deploy PicPeak in production: + +```bash +# 1. Clone the repository +git clone https://github.com/the-luap/picpeak.git +cd picpeak + +# 2. Use the automated install script (recommended) +sudo ./scripts/install.sh + +# Or manually: +# 2. Copy production environment template +cp .env.production.example .env + +# 3. Generate and add JWT secret +echo "JWT_SECRET=$(openssl rand -base64 32)" >> .env + +# 4. Edit configuration +nano .env # Update all required values + +# 5. Create directories +mkdir -p storage/events/active storage/events/archived storage/thumbnails storage/uploads +mkdir -p data logs certbot/conf certbot/www + +# 6. Deploy +docker-compose -f docker-compose.prod.yml up -d + +# 7. Check logs +docker-compose -f docker-compose.prod.yml logs -f +``` + +## 📦 Deployment Methods + +### Method 1: Docker Compose (Recommended) + +#### Step 1: Environment Configuration + +Create `.env` file with all required variables: + +```env +# SECURITY - MUST CHANGE ALL! +JWT_SECRET= +DB_PASSWORD= + +# Application URLs +FRONTEND_URL=https://your-domain.com +BACKEND_URL=https://your-domain.com +ADMIN_URL=https://your-domain.com + +# Database (PostgreSQL for Docker) +DATABASE_CLIENT=pg +DB_HOST=db +DB_PORT=5432 +DB_NAME=picpeak +DB_USER=picpeak + +# Email (Example: SendGrid) +SMTP_HOST=smtp.sendgrid.net +SMTP_PORT=587 +SMTP_SECURE=false +SMTP_USER=apikey +SMTP_PASS=your-sendgrid-api-key +EMAIL_FROM=PicPeak + +# Application +NODE_ENV=production +PORT=3001 +LOG_LEVEL=info +``` + +#### Step 2: Docker Volume Permissions + +Create `docker-compose.override.yml` for proper permissions: + +```yaml +version: '3.8' + +services: + backend: + volumes: + - ./storage:/app/storage:delegated + - ./data:/app/data:delegated + - ./logs:/app/logs:delegated + user: "1001:1001" # nodejs user + + db: + volumes: + - ./postgres-data:/var/lib/postgresql/data +``` + +#### Step 3: Build and Deploy + +```bash +# Set correct permissions +chmod -R 755 storage data logs + +# Build images +docker-compose -f docker-compose.prod.yml build + +# Start services +docker-compose -f docker-compose.prod.yml up -d + +# Run database migrations +docker-compose -f docker-compose.prod.yml exec backend npm run migrate + +# Admin credentials will be displayed and saved to ADMIN_CREDENTIALS.txt +``` + +#### Step 4: Configure Nginx + +Update `nginx/sites-enabled/default` with your domain: + +```nginx +server { + listen 80; + server_name your-domain.com; + + # Redirect to HTTPS + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + server_name your-domain.com; + + # SSL configuration (managed by Certbot) + ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; + + # Frontend + location / { + proxy_pass http://frontend:80; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # API proxy + location /api { + proxy_pass http://backend:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # Protected images + location /photos { + proxy_pass http://backend:3000; + proxy_set_header Host $host; + } + + # Thumbnails + location /thumbnails { + proxy_pass http://backend:3000; + proxy_set_header Host $host; + } + + # Public uploads + location /uploads { + proxy_pass http://backend:3000; + proxy_set_header Host $host; + } +} +``` + +### Method 2: PM2 (Node.js Process Manager) + +#### Step 1: Install Dependencies + +```bash +# Install Node.js 18+ +curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - +sudo apt-get install -y nodejs + +# Install PostgreSQL +sudo apt-get install -y postgresql postgresql-contrib + +# Install PM2 globally +sudo npm install -g pm2 + +# Install nginx (if using reverse proxy) +sudo apt-get install -y nginx +``` + +#### Step 2: Setup Database + +```bash +# Create database and user +sudo -u postgres psql +CREATE DATABASE picpeak; +CREATE USER picpeak WITH ENCRYPTED PASSWORD 'your-secure-password'; +GRANT ALL PRIVILEGES ON DATABASE picpeak TO picpeak; +\q +``` + +#### Step 3: Clone and Configure + +```bash +# Clone repository +git clone https://github.com/the-luap/picpeak.git +cd picpeak + +# Install dependencies +cd backend && npm install +cd ../frontend && npm install + +# Configure environment +cd .. +cp .env.production.example .env +nano .env # Update all values +``` + +#### Step 4: Build Frontend + +```bash +cd frontend +npm run build +cd .. +``` + +#### Step 5: Start with PM2 + +```bash +cd backend + +# Start application +pm2 start ecosystem.config.js + +# Save PM2 configuration +pm2 save + +# Setup startup script +pm2 startup +``` + +#### Step 6: Configure Nginx + +Create `/etc/nginx/sites-available/picpeak`: + +```nginx +server { + listen 80; + server_name your-domain.com; + + # Frontend (static files) + location / { + root /path/to/picpeak/frontend/dist; + try_files $uri $uri/ /index.html; + } + + # API proxy + location /api { + proxy_pass http://localhost:3001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + # Protected photos + location /photos { + proxy_pass http://localhost:3001; + proxy_set_header Host $host; + } + + # Other proxied paths + location ~ ^/(thumbnails|uploads) { + proxy_pass http://localhost:3001; + proxy_set_header Host $host; + } +} +``` + +Enable the site: +```bash +sudo ln -s /etc/nginx/sites-available/picpeak /etc/nginx/sites-enabled/ +sudo nginx -t +sudo systemctl restart nginx +``` + +### Method 3: Manual Installation + +Similar to PM2 method but using systemd instead: + +#### Create Systemd Service + +Create `/etc/systemd/system/picpeak.service`: + +```ini +[Unit] +Description=PicPeak Photo Sharing +After=network.target + +[Service] +Type=simple +User=picpeak +WorkingDirectory=/home/picpeak/picpeak/backend +ExecStart=/usr/bin/node server.js +Restart=on-failure +Environment="NODE_ENV=production" + +[Install] +WantedBy=multi-user.target +``` + +Start the service: +```bash +sudo systemctl daemon-reload +sudo systemctl enable picpeak +sudo systemctl start picpeak +``` + +### Method 4: Without Nginx (Direct Access) + +For deployments without a reverse proxy: + +#### Option A: Direct Backend Access + +1. **Configure environment for direct access**: +```env +# .env +FRONTEND_URL=http://your-domain.com:5173 +BACKEND_URL=http://your-domain.com:3001 +ADMIN_URL=http://your-domain.com:5173 + +# Enable CORS for direct access +CORS_ENABLED=true +``` + +2. **Run backend directly**: +```bash +cd backend +NODE_ENV=production node server.js +``` + +3. **Run frontend development server** (not recommended for production): +```bash +cd frontend +VITE_API_URL=http://your-domain.com:3001/api npm run dev -- --host +``` + +#### Option B: Backend Serves Frontend + +1. **Build frontend**: +```bash +cd frontend +VITE_API_URL=/api npm run build +``` + +2. **Configure backend to serve frontend**: +```javascript +// Add to backend/server.js after API routes +if (process.env.NODE_ENV === 'production') { + app.use(express.static(path.join(__dirname, '../frontend/dist'))); + app.get('*', (req, res) => { + res.sendFile(path.join(__dirname, '../frontend/dist/index.html')); + }); +} +``` + +3. **Access everything on backend port**: +```bash +# Application available at http://your-domain.com:3001 +NODE_ENV=production node server.js +``` + +#### Option C: Using Node.js HTTP Proxy + +Create a simple proxy server: + +```javascript +// proxy-server.js +const express = require('express'); +const { createProxyMiddleware } = require('http-proxy-middleware'); +const path = require('path'); + +const app = express(); + +// Serve frontend static files +app.use(express.static(path.join(__dirname, 'frontend/dist'))); + +// Proxy API requests +app.use('/api', createProxyMiddleware({ + target: 'http://localhost:3001', + changeOrigin: true +})); + +// Proxy other backend routes +app.use(['/photos', '/thumbnails', '/uploads'], createProxyMiddleware({ + target: 'http://localhost:3001', + changeOrigin: true +})); + +// Catch all - serve frontend +app.get('*', (req, res) => { + res.sendFile(path.join(__dirname, 'frontend/dist/index.html')); +}); + +app.listen(80); +``` + +## 🔧 Environment Configuration + +### Required Environment Variables + +| Variable | Description | Example | +|----------|-------------|---------| +| `JWT_SECRET` | **CRITICAL** - Authentication secret (min 32 chars) | Use `openssl rand -base64 32` | +| `DATABASE_CLIENT` | Database type | `pg` for PostgreSQL, `sqlite3` for SQLite | +| `DB_HOST` | Database host | `localhost` or `db` (Docker) | +| `DB_PORT` | Database port | `5432` | +| `DB_NAME` | Database name | `picpeak` | +| `DB_USER` | Database user | `picpeak` | +| `DB_PASSWORD` | Database password | Strong password | +| `SMTP_HOST` | Email server | `smtp.gmail.com` | +| `SMTP_PORT` | Email port | `587` | +| `SMTP_USER` | Email username | `your-email@gmail.com` | +| `SMTP_PASS` | Email password | App-specific password | +| `EMAIL_FROM` | From address | `PicPeak ` | +| `FRONTEND_URL` | Frontend URL | `https://your-domain.com` | +| `BACKEND_URL` | Backend URL | `https://your-domain.com` | + +### Optional Configuration + +| Variable | Description | Default | +|----------|-------------|---------| +| `NODE_ENV` | Environment | `production` | +| `PORT` | Backend port | `3001` | +| `LOG_LEVEL` | Logging level | `info` | +| `SESSION_TIMEOUT_MINUTES` | Session timeout | `60` | +| `RATE_LIMIT_WINDOW_MS` | Rate limit window | `900000` (15 min) | +| `RATE_LIMIT_MAX_REQUESTS` | Max requests | `100` | +| `DB_POOL_MIN` | Min DB connections | `5` | +| `DB_POOL_MAX` | Max DB connections | `25` | +| `DEFAULT_EXPIRATION_DAYS` | Gallery expiration | `30` | +| `WARNING_DAYS_BEFORE_EXPIRY` | Warning period | `7` | + +### Frontend Environment + +For production builds: +```bash +# frontend/.env.production +VITE_API_URL=/api # For reverse proxy +# or +VITE_API_URL=https://api.your-domain.com # For direct access +``` + +## 👤 Admin Setup + +### Automatic Admin Creation + +When you run migrations for the first time, an admin account is automatically created: + +```bash +# Docker +docker-compose -f docker-compose.prod.yml exec backend npm run migrate + +# PM2/Manual +cd backend && npm run migrate +``` + +Output: +``` +======================================== +✅ Admin user created successfully! +======================================== +Username: admin +Password: SwiftEagle3847! + +⚠️ IMPORTANT: Change password on first login +======================================== +``` + +### Important Admin Notes + +1. **Credentials are saved** to `backend/ADMIN_CREDENTIALS.txt` +2. **Must change password** on first login (enforced) +3. **Password requirements**: + - Minimum 12 characters + - Uppercase and lowercase letters + - Numbers and special characters + - Not a common password + +### Lost Admin Password + +```bash +# Docker +docker-compose -f docker-compose.prod.yml exec backend node scripts/reset-admin-password.js + +# PM2/Manual +cd backend && node scripts/reset-admin-password.js +``` + +## 🔒 SSL/HTTPS Configuration + +### Option 1: Let's Encrypt with Certbot + +```bash +# Initial certificate +docker-compose -f docker-compose.prod.yml run --rm certbot certonly \ + --webroot --webroot-path=/var/www/certbot \ + -d your-domain.com -d www.your-domain.com + +# Auto-renewal is handled by certbot container +``` + +### Option 2: Using Traefik + +Add to `docker-compose.override.yml`: + +```yaml +services: + frontend: + labels: + - "traefik.enable=true" + - "traefik.http.routers.picpeak.rule=Host(`your-domain.com`)" + - "traefik.http.routers.picpeak.entrypoints=websecure" + - "traefik.http.routers.picpeak.tls.certresolver=letsencrypt" +``` + +### Option 3: CloudFlare or Other CDN + +1. Set up your domain in CloudFlare +2. Enable "Full SSL/TLS encryption mode" +3. Use CloudFlare's origin certificates + +## 🔧 Maintenance & Operations + +### Backup Procedures + +Use the included backup script or create your own: + +```bash +# Use the provided backup script +./scripts/backup.sh + +# Or create custom backup script: +#!/bin/bash +DATE=$(date +%Y%m%d_%H%M%S) +BACKUP_DIR="./backups/$DATE" + +mkdir -p $BACKUP_DIR + +# Database backup +docker-compose -f docker-compose.prod.yml exec -T db \ + pg_dump -U picpeak picpeak > $BACKUP_DIR/database.sql + +# Files backup +tar -czf $BACKUP_DIR/storage.tar.gz storage/ + +echo "Backup completed: $BACKUP_DIR" +``` + +### Automated Backups + +The application includes a built-in backup service. Configure via Admin Panel: +- Settings → Backup Configuration +- Set schedule (cron expression) +- Configure destination (local, rsync, S3) +- Enable email notifications + +### Updates + +```bash +# Docker method +git pull +docker-compose -f docker-compose.prod.yml build +docker-compose -f docker-compose.prod.yml up -d + +# PM2 method +git pull +cd backend && npm install +cd ../frontend && npm install && npm run build +pm2 restart picpeak +``` + +### Monitoring + +#### Health Checks +```bash +# API health +curl https://your-domain.com/api/health + +# Database connection +docker-compose -f docker-compose.prod.yml exec backend \ + psql -U picpeak -d picpeak -c "SELECT 1" + +# Service status +docker-compose -f docker-compose.prod.yml ps +``` + +#### Logs +```bash +# Docker logs +docker-compose -f docker-compose.prod.yml logs -f + +# PM2 logs +pm2 logs picpeak + +# System logs +tail -f /var/log/nginx/error.log +``` + +## 🚨 Troubleshooting + +### Common Issues + +#### JWT Secret Errors +**Error**: "Missing required environment variable: JWT_SECRET" +- **Solution**: Set JWT_SECRET in your .env file +- **Generate**: `openssl rand -base64 32` + +**Error**: "JWT_SECRET is set to the insecure default value" +- **Solution**: Change from default to secure value + +#### Database Connection Failed +**Error**: "connect ECONNREFUSED" +- **Check**: Database is running +- **Check**: Correct host/port in .env +- **Docker**: Use `db` as host, not `localhost` + +#### Permission Errors +**Error**: "EACCES: permission denied" +```bash +# Fix Docker permissions +sudo chown -R 1001:1001 storage data logs + +# Fix PM2/Manual permissions +sudo chown -R $USER:$USER storage data logs +chmod -R 755 storage +``` + +#### Email Not Sending +- **Check**: SMTP credentials are correct +- **Gmail**: Use app-specific password +- **Test**: Admin Panel → Settings → Email → Test Email +- **Logs**: Check `email_queue` table for errors + +#### Photos Not Appearing +- **Check**: File watcher is running +- **Permissions**: `chmod -R 755 storage/` +- **Logs**: `grep watcher` in backend logs + +#### Frontend Can't Connect to Backend +- **CORS**: Ensure FRONTEND_URL matches in backend .env +- **Proxy**: Check nginx configuration +- **Direct**: Set CORS_ENABLED=true for non-proxy setup + +### Debug Commands + +```bash +# Check all services +docker-compose -f docker-compose.prod.yml ps + +# Backend shell access +docker-compose -f docker-compose.prod.yml exec backend sh + +# Database access +docker-compose -f docker-compose.prod.yml exec db psql -U picpeak + +# Test API +curl -I http://localhost:3001/api/health + +# Check disk space +df -h storage/ + +# View running processes +ps aux | grep node +``` + +## ✅ Security Checklist + +- [ ] **JWT_SECRET** is randomly generated (min 32 chars) +- [ ] **Database password** is strong and unique +- [ ] **Admin password** changed from auto-generated +- [ ] **SSL/HTTPS** enabled and working +- [ ] **Firewall** configured (only 80/443 open) +- [ ] **File permissions** set correctly (755 for storage) +- [ ] **Rate limiting** enabled (default: 100 req/15min) +- [ ] **CORS** properly configured +- [ ] **Environment files** not in version control +- [ ] **Backups** configured and tested +- [ ] **Monitoring** alerts set up +- [ ] **Updates** scheduled regularly +- [ ] **Access logs** being monitored +- [ ] **Email** using app-specific passwords +- [ ] **Umami analytics** configured (optional) + +## 📞 Support + +- 📘 [Documentation](https://github.com/the-luap/picpeak) +- 🐛 [Report Issues](https://github.com/the-luap/picpeak/issues) +- 💬 [Discussions](https://github.com/the-luap/picpeak/discussions) + +--- + +**Need help?** Check the logs first, then open an issue with: +- Deployment method used +- Error messages +- Relevant log output +- Environment (without secrets) \ No newline at end of file diff --git a/PRODUCTION_DEPLOYMENT_GUIDE.md b/PRODUCTION_DEPLOYMENT_GUIDE.md deleted file mode 100644 index 9718db6..0000000 --- a/PRODUCTION_DEPLOYMENT_GUIDE.md +++ /dev/null @@ -1,378 +0,0 @@ -# Production Deployment Guide - -This comprehensive guide addresses all production deployment scenarios and common issues. - -## Pre-Deployment Checklist - -### 1. Environment Variables -Create a `.env` file with ALL required variables: - -```bash -# CRITICAL - Must change these! -JWT_SECRET= -DB_PASSWORD= - -# Application URLs (your actual domain) -ADMIN_URL=https://yourdomain.com -FRONTEND_URL=https://yourdomain.com -BACKEND_URL=https://yourdomain.com - -# Database (PostgreSQL) -DATABASE_CLIENT=pg -DB_HOST=postgres # or external host -DB_PORT=5432 -DB_USER=picpeak -DB_NAME=picpeak - -# Email Configuration (required for notifications) -SMTP_HOST=smtp.gmail.com -SMTP_PORT=587 -SMTP_SECURE=false -SMTP_USER=your-email@gmail.com -SMTP_PASS=your-app-password # Use app-specific password -EMAIL_FROM=PicPeak - -# Port Configuration -PORT=3001 - -# Performance Tuning -DB_POOL_MIN=5 -DB_POOL_MAX=25 -NODE_ENV=production -LOG_LEVEL=info - -# Optional: Umami Analytics (configured via Admin UI) -# UMAMI_URL=https://analytics.yourdomain.com -# UMAMI_WEBSITE_ID=your-website-id -``` - -### 2. Generate Secrets - -```bash -# Generate JWT Secret (REQUIRED) -openssl rand -base64 32 - -# Generate Database Password -openssl rand -base64 24 -``` - -## Frontend Configuration - -For production deployment behind a reverse proxy: - -### Frontend Environment -```bash -# frontend/.env.production -VITE_API_URL=/api # Uses relative path for reverse proxy - -# Optional: Umami fallback (primary config via Admin UI) -# VITE_UMAMI_URL=https://analytics.yourdomain.com -# VITE_UMAMI_WEBSITE_ID=your-website-id -``` - -This ensures all API calls use the same domain/protocol as the frontend. - -### Nginx Proxy Configuration - -The frontend nginx configuration already includes proper proxy settings for: -- `/api` → Backend API -- `/photos` → Protected photo access -- `/thumbnails` → Thumbnail images -- `/uploads` → Public uploads (logos, favicons) - -All static assets are served through the nginx proxy, inheriting authentication headers. - -## Deployment Steps - -### 1. Initial Setup - -```bash -# Clone repository -git clone https://github.com/the-luap/wedding-photo-sharing.git -cd wedding-photo-sharing - -# Create required directories -mkdir -p storage/events/active storage/events/archived storage/thumbnails storage/uploads -mkdir -p data logs -mkdir -p certbot/conf certbot/www - -# Set permissions (important!) -chmod -R 755 storage data logs -``` - -### 2. Fix Docker Volume Permissions - -Create `docker-compose.override.yml` for local volume configuration: - -```yaml -version: '3.8' - -services: - backend: - volumes: - - ./storage:/app/storage:delegated - - ./data:/app/data:delegated - - ./logs:/app/logs:delegated - user: "1001:1001" # nodejs user - - db: - volumes: - - ./postgres-data:/var/lib/postgresql/data -``` - -### 3. Build and Deploy - -```bash -# Build images -docker-compose -f docker-compose.prod.yml build - -# Start services -docker-compose -f docker-compose.prod.yml up -d - -# Check logs -docker-compose -f docker-compose.prod.yml logs -f backend -``` - -### 4. Initial Admin Setup - -The admin user is automatically created during database migration: - -```bash -# Run migrations (this creates admin user) -docker-compose -f docker-compose.prod.yml exec backend npm run migrate - -# Admin credentials will be displayed in console and saved to ADMIN_CREDENTIALS.txt -# Example output: -# ======================================== -# ✅ Admin user created successfully! -# ======================================== -# Username: admin -# Password: SwiftEagle3847! -# -# ⚠️ IMPORTANT: Change password on first login -# ======================================== - -# Retrieve credentials if needed -docker-compose -f docker-compose.prod.yml exec backend cat ADMIN_CREDENTIALS.txt -``` - -**Important**: You MUST change the auto-generated password on first login. - -### 5. Configure Email (if using database config) - -1. Login to admin panel: https://yourdomain.com/admin -2. Go to Settings > Email Configuration -3. Enter SMTP details -4. Test email sending - -## Common Issues and Solutions - -### Issue 1: Migration Failures - -**Error**: "relation already exists" - -**Solution**: The safe migration runner handles this automatically. If issues persist: - -```bash -# Reset migrations tracking -docker-compose -f docker-compose.prod.yml exec db psql -U picpeak -d picpeak - -# In PostgreSQL: -DROP TABLE IF EXISTS migrations; -\q - -# Re-run migrations -docker-compose -f docker-compose.prod.yml exec backend npm run migrate:safe -``` - -### Issue 2: Permission Denied Errors - -**Error**: "EACCES: permission denied" - -**Solution**: Fix container permissions: - -```bash -# Stop containers -docker-compose -f docker-compose.prod.yml down - -# Fix permissions on host -sudo chown -R 1001:1001 storage data logs - -# Restart -docker-compose -f docker-compose.prod.yml up -d -``` - -### Issue 3: Database Connection Failed - -**Error**: "no pg_hba.conf entry" - -**Solution**: Already fixed in docker-compose.prod.yml with: -- SSL disabled for internal Docker network -- Proper authentication method (scram-sha-256) - -### Issue 4: Frontend Can't Connect to Backend - -**Error**: CORS errors or connection refused - -**Solution**: Ensure environment variables match: -- Backend: `FRONTEND_URL` must match your frontend URL -- Frontend: `VITE_API_URL` must be set during build - -### Issue 5: Email Not Sending - -**Solution**: Check email configuration: - -```bash -# Check backend logs -docker-compose -f docker-compose.prod.yml logs backend | grep email - -# Verify SMTP settings -# Gmail users: Use app password, not regular password -# Enable "Less secure app access" or use OAuth2 -``` - -## SSL/HTTPS Setup - -### Option 1: Using Traefik (Recommended) - -Add these labels to your docker-compose override: - -```yaml -services: - frontend: - labels: - - "traefik.enable=true" - - "traefik.http.routers.picpeak.rule=Host(`yourdomain.com`)" - - "traefik.http.routers.picpeak.entrypoints=websecure" - - "traefik.http.routers.picpeak.tls.certresolver=letsencrypt" - - "traefik.http.services.picpeak.loadbalancer.server.port=80" -``` - -### Option 2: Using Certbot - -1. Update `nginx/sites-enabled/default` with your domain -2. Run certbot: - -```bash -# Initial certificate -docker-compose -f docker-compose.prod.yml run --rm certbot certonly \ - --webroot --webroot-path=/var/www/certbot \ - -d yourdomain.com -d www.yourdomain.com - -# Auto-renewal is handled by the certbot container -``` - -## Monitoring - -### Health Checks - -```bash -# Backend health -curl http://localhost/api/health - -# Database connection -docker-compose -f docker-compose.prod.yml exec backend \ - psql -U picpeak -d picpeak -c "SELECT 1" -``` - -### Logs - -```bash -# All services -docker-compose -f docker-compose.prod.yml logs -f - -# Specific service -docker-compose -f docker-compose.prod.yml logs -f backend -``` - -## Backup and Restore - -### Backup - -```bash -#!/bin/bash -# backup.sh -DATE=$(date +%Y%m%d_%H%M%S) -BACKUP_DIR="./backups/$DATE" - -mkdir -p $BACKUP_DIR - -# Database -docker-compose -f docker-compose.prod.yml exec -T db \ - pg_dump -U picpeak picpeak > $BACKUP_DIR/database.sql - -# Files -tar -czf $BACKUP_DIR/storage.tar.gz storage/ - -echo "Backup completed: $BACKUP_DIR" -``` - -### Restore - -```bash -# Database -docker-compose -f docker-compose.prod.yml exec -T db \ - psql -U picpeak picpeak < ./backups/20240713_120000/database.sql - -# Files -tar -xzf ./backups/20240713_120000/storage.tar.gz -``` - -## Production Best Practices - -1. **Always use named volumes** in production for better data persistence -2. **Set up monitoring** with Prometheus/Grafana -3. **Enable backups** with automated scripts -4. **Use a reverse proxy** (Nginx) for SSL termination -5. **Implement rate limiting** at the Nginx level -6. **Regular updates** - Keep Docker images updated -7. **Log rotation** - Configure log rotation for application logs - -## Troubleshooting Commands - -```bash -# Check running containers -docker-compose -f docker-compose.prod.yml ps - -# Restart a service -docker-compose -f docker-compose.prod.yml restart backend - -# View real-time logs -docker-compose -f docker-compose.prod.yml logs -f --tail=100 - -# Execute commands in container -docker-compose -f docker-compose.prod.yml exec backend sh - -# Database shell -docker-compose -f docker-compose.prod.yml exec db psql -U picpeak - -# Clean restart -docker-compose -f docker-compose.prod.yml down -docker-compose -f docker-compose.prod.yml up -d -``` - -## Security Checklist - -- [ ] Strong JWT_SECRET (min 32 chars) -- [ ] Strong database password -- [ ] Admin password changed from auto-generated one -- [ ] SSL/HTTPS enabled -- [ ] Firewall configured (only 80/443 open) -- [ ] Regular security updates -- [ ] Backup encryption -- [ ] Access logs monitored -- [ ] Rate limiting enabled (built-in) -- [ ] File upload restrictions configured -- [ ] Password complexity requirements configured (Admin > Settings) -- [ ] Session timeout configured (default 60 min) -- [ ] Umami analytics configured (if using) -- [ ] SMTP credentials secured with app-specific password - -## Support - -For issues not covered here: -1. Check application logs -2. Review error messages carefully -3. Ensure all environment variables are set -4. Verify file permissions -5. Check Docker daemon logs \ No newline at end of file diff --git a/migrations/005_add_watermark_settings.js b/migrations/005_add_watermark_settings.js deleted file mode 100644 index 0a36d59..0000000 --- a/migrations/005_add_watermark_settings.js +++ /dev/null @@ -1,61 +0,0 @@ -const knex = require('knex'); - -exports.up = async function(db) { - console.log('Adding watermark settings...'); - - // Add watermark settings to app_settings table - const watermarkSettings = [ - { - setting_key: 'branding_watermark_logo_path', - setting_value: JSON.stringify(null), - setting_type: 'branding' - }, - { - setting_key: 'branding_watermark_logo_url', - setting_value: JSON.stringify(null), - setting_type: 'branding' - }, - { - setting_key: 'branding_watermark_position', - setting_value: JSON.stringify('bottom-right'), - setting_type: 'branding' - }, - { - setting_key: 'branding_watermark_opacity', - setting_value: JSON.stringify(50), - setting_type: 'branding' - }, - { - setting_key: 'branding_watermark_size', - setting_value: JSON.stringify(15), - setting_type: 'branding' - } - ]; - - for (const setting of watermarkSettings) { - // Check if setting already exists - const existing = await db('app_settings') - .where('setting_key', setting.setting_key) - .first(); - - if (!existing) { - await db('app_settings').insert(setting); - console.log(`Added setting: ${setting.setting_key}`); - } - } - - console.log('Watermark settings migration completed'); -}; - -exports.down = async function(db) { - // Remove watermark settings - await db('app_settings') - .whereIn('setting_key', [ - 'branding_watermark_logo_path', - 'branding_watermark_logo_url', - 'branding_watermark_position', - 'branding_watermark_opacity', - 'branding_watermark_size' - ]) - .del(); -}; \ No newline at end of file diff --git a/test-maintenance.sh b/test-maintenance.sh deleted file mode 100755 index ccf5de2..0000000 --- a/test-maintenance.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# Test maintenance mode functionality - -echo "Testing maintenance mode implementation..." - -# First, let's check the current maintenance mode status -echo -e "\n1. Checking current maintenance mode status:" -curl -s http://localhost:3002/api/public/settings | jq '.general_maintenance_mode' - -# Test a public gallery endpoint -echo -e "\n2. Testing public gallery endpoint (should get 503 if maintenance is on):" -curl -s -o /dev/null -w "%{http_code}" http://localhost:3002/api/gallery/test-gallery/info - -# Test admin login (should always work) -echo -e "\n\n3. Testing admin login endpoint (should always work):" -curl -s -o /dev/null -w "%{http_code}" http://localhost:3002/api/admin/login - -echo -e "\n\nDone!" \ No newline at end of file