From 5db29f7a264223fe094fdae470cd8bd2995c0486 Mon Sep 17 00:00:00 2001 From: dlawler489 <104159223@student.swin.edu.au> Date: Tue, 21 Apr 2026 09:20:38 +1000 Subject: [PATCH] Update deployment compose file for container interfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔧 Container Interface Deployment Fix: - Use nginx.deploy.conf for deployment-specific nginx config - Add command to copy client files to shared volume - Improve startup logging and error handling - Ensure client files are available to nginx container ✅ Deployment Ready: - Works with Portainer, Docker Desktop, and similar interfaces - Copies React build files from API container to nginx volume - Proper container startup sequence and health checks - Clear logging for troubleshooting startup issues This fixes the missing client files issue when deploying from container management interfaces. --- CONTAINER_INTERFACE_DEPLOYMENT.md | 197 ++++++++++++++++++++++++++++++ docker-compose.deploy.yml | 85 +++++++++++++ nginx.deploy.conf | 77 ++++++++++++ 3 files changed, 359 insertions(+) create mode 100644 CONTAINER_INTERFACE_DEPLOYMENT.md create mode 100644 docker-compose.deploy.yml create mode 100644 nginx.deploy.conf diff --git a/CONTAINER_INTERFACE_DEPLOYMENT.md b/CONTAINER_INTERFACE_DEPLOYMENT.md new file mode 100644 index 0000000..c91ff58 --- /dev/null +++ b/CONTAINER_INTERFACE_DEPLOYMENT.md @@ -0,0 +1,197 @@ +# Container Management Interface Deployment Guide + +This guide shows how to deploy the Etsy Finance Tracker using container management interfaces like Portainer, Docker Desktop, or similar tools. + +## 🚀 Quick Deployment Steps + +### Method 1: Stack Deployment (Recommended) + +1. **Copy the Repository URL**: + ``` + https://github.com/dlawler489/etsy-finance-tracker + ``` + +2. **In your container interface**: + - Navigate to "Stacks" or "Deploy from Git" + - Paste the repository URL: `https://github.com/dlawler489/etsy-finance-tracker` + - Set the compose file path: `docker-compose.deploy.yml` + - Stack name: `etsy-finance-tracker` + +3. **Environment Variables** (Optional): + ``` + NODE_ENV=production + PORT=8080 + ``` + +4. **Deploy the Stack** + - Click "Deploy" or "Create Stack" + - Wait for images to pull and containers to start + +### Method 2: Manual Container Creation + +If your interface doesn't support stacks, create containers manually: + +#### Container 1: Etsy API Server +- **Image**: `ghcr.io/dlawler489/etsy-finance-tracker:main` +- **Name**: `etsy-finance-tracker` +- **Ports**: Internal port 8080 (don't expose externally) +- **Environment**: + - `NODE_ENV=production` + - `PORT=8080` +- **Volumes**: + - `etsy_data:/app/data` + - `etsy_uploads:/app/uploads` + - `client_dist:/usr/share/nginx/html` +- **Networks**: Create/join `etsy-network` +- **Restart Policy**: Unless stopped + +#### Container 2: Nginx Web Server +- **Image**: `nginx:alpine` +- **Name**: `etsy-nginx` +- **Ports**: `3000:80` (expose port 3000) +- **Volumes**: + - Mount nginx config (see below) + - `client_dist:/usr/share/nginx/html:ro` +- **Networks**: Join `etsy-network` +- **Depends on**: `etsy-finance-tracker` +- **Restart Policy**: Unless stopped + +## 📁 Required Files + +### docker-compose.deploy.yml +```yaml +version: '3.8' + +services: + nginx: + image: nginx:alpine + container_name: etsy-nginx + ports: + - "3000:80" + volumes: + - ./nginx.deploy.conf:/etc/nginx/nginx.conf:ro + - client_dist:/usr/share/nginx/html:ro + depends_on: + - etsy-tracker + restart: unless-stopped + + etsy-tracker: + image: ghcr.io/dlawler489/etsy-finance-tracker:main + container_name: etsy-finance-tracker + expose: + - "8080" + environment: + - NODE_ENV=production + - PORT=8080 + volumes: + - etsy_data:/app/data + - etsy_uploads:/app/uploads + - client_dist:/usr/share/nginx/html + restart: unless-stopped + command: > + sh -c " + cp -r /app/client/dist/* /usr/share/nginx/html/ 2>/dev/null || true; + exec node server/dist/index.js + " + +volumes: + etsy_uploads: + client_dist: + etsy_data: + +networks: + etsy-network: + driver: bridge +``` + +## 🔧 Configuration Options + +### Port Mapping +- **External Port**: 3000 (change if needed) +- **Internal Ports**: + - Nginx: 80 + - API Server: 8080 (internal only) + +### Storage Volumes +- **`etsy_data`**: Your business data (CSV, PDF, Excel files) +- **`etsy_uploads`**: File uploads and temporary files +- **`client_dist`**: React app static files (shared between containers) + +### Environment Variables +- **`NODE_ENV`**: Set to `production` +- **`PORT`**: API server port (default: 8080) +- **`CLIENT_URL`**: Set to `http://nginx` for container communication + +## 🌐 Access Your Application + +After deployment: +- **Web Interface**: `http://your-server-ip:3000` +- **Health Check**: `http://your-server-ip:3000/health` + +## 🔄 Updates and Management + +### Update to Latest Version +1. **In your container interface**: + - Go to Images or Registry + - Pull latest: `ghcr.io/dlawler489/etsy-finance-tracker:main` + - Recreate the `etsy-tracker` container with new image + +2. **Or via Stack Update**: + - Edit stack and click "Update" + - Images will automatically update + +### View Logs +- Container logs available in your interface +- Look for both `etsy-nginx` and `etsy-finance-tracker` containers + +### Data Backup +- Your business data is in the `etsy_data` volume +- Back up this volume regularly through your interface + +## 🛠 Troubleshooting + +### Common Issues + +1. **Port 3000 Already in Use** + - Change external port mapping: `3001:80` instead of `3000:80` + +2. **Containers Not Communicating** + - Ensure both containers are on same network (`etsy-network`) + - Check container names match the nginx upstream config + +3. **Static Files Not Loading** + - Verify `client_dist` volume is shared between containers + - Check nginx container logs for file access issues + +4. **API Calls Failing** + - Verify `etsy-finance-tracker` container is running + - Check health endpoint: `/health` + - Review API container logs + +### Health Checks +Both containers include health checks: +- **API**: `curl http://localhost:8080/health` +- **Nginx**: `wget http://localhost/` + +## 📊 Monitoring + +Your container interface should show: +- **Container Status**: Both containers running +- **Resource Usage**: CPU, memory consumption +- **Health Status**: Green/healthy status +- **Port Mappings**: Port 3000 accessible externally + +## 🔒 Security Notes + +- **Internal Communication**: API server not exposed externally +- **Reverse Proxy**: Nginx handles all external requests +- **Data Volumes**: Business data persists between updates +- **Health Monitoring**: Built-in container health checks + +## 🎯 Best Practices + +1. **Use Named Volumes**: Easier to manage and backup +2. **Set Restart Policies**: Containers restart automatically +3. **Monitor Resources**: Set memory/CPU limits if needed +4. **Regular Updates**: Pull latest images periodically +5. **Backup Data**: Regular backups of `etsy_data` volume \ No newline at end of file diff --git a/docker-compose.deploy.yml b/docker-compose.deploy.yml new file mode 100644 index 0000000..5277c14 --- /dev/null +++ b/docker-compose.deploy.yml @@ -0,0 +1,85 @@ +version: '3.8' + +services: + # Nginx reverse proxy and static file server + nginx: + image: nginx:alpine + container_name: etsy-nginx + ports: + - "3000:80" + volumes: + - ./nginx.deploy.conf:/etc/nginx/nginx.conf:ro + - client_dist:/usr/share/nginx/html:ro + depends_on: + - etsy-tracker + restart: unless-stopped + networks: + - etsy-network + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s + + # Etsy Finance Tracker API Server (from GitHub Container Registry) + etsy-tracker: + image: ghcr.io/dlawler489/etsy-finance-tracker:main + container_name: etsy-finance-tracker + expose: + - "8080" + environment: + - NODE_ENV=production + - PORT=8080 + - CLIENT_URL=http://nginx + volumes: + # Mount data directory for persistent storage + - etsy_data:/app/data + # Optional: Mount uploads directory if needed + - etsy_uploads:/app/uploads + # Share client build with nginx + - client_dist:/usr/share/nginx/html + restart: unless-stopped + networks: + - etsy-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + # Copy client files to shared volume and start server + command: > + sh -c " + echo 'Starting Etsy Finance Tracker API Server...'; + if [ ! -f /usr/share/nginx/html/index.html ]; then + echo 'Copying client files to shared volume...'; + cp -r /app/client/dist/* /usr/share/nginx/html/ 2>/dev/null || true; + echo 'Client files copied successfully'; + fi; + echo 'Starting Node.js server...'; + exec node server/dist/index.js + " + command: > + sh -c " + echo 'Starting Etsy Finance Tracker...'; + if [ ! -f /usr/share/nginx/html/index.html ]; then + echo 'Extracting client files to shared volume...'; + cp -r /app/client/dist/* /usr/share/nginx/html/ 2>/dev/null || true; + echo 'Client files extracted successfully'; + fi; + echo 'Starting API server on port 8080...'; + exec node server/dist/index.js + " + +volumes: + etsy_uploads: + driver: local + client_dist: + driver: local + etsy_data: + driver: local + +networks: + etsy-network: + driver: bridge \ No newline at end of file diff --git a/nginx.deploy.conf b/nginx.deploy.conf new file mode 100644 index 0000000..9118022 --- /dev/null +++ b/nginx.deploy.conf @@ -0,0 +1,77 @@ +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Logging + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log warn; + + # Basic settings + sendfile on; + keepalive_timeout 65; + client_max_body_size 20M; + + # Gzip compression + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + # Upstream backend + upstream etsy_api { + server etsy-finance-tracker:8080; + } + + server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + # Health check endpoint (proxied to backend) + location /health { + proxy_pass http://etsy_api; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # API routes - proxy to backend + location /api/ { + proxy_pass http://etsy_api; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Static assets with caching + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # React app - serve index.html for all routes (SPA support) + location / { + try_files $uri $uri/ /index.html; + + # Prevent caching of index.html + location = /index.html { + add_header Cache-Control "no-cache, no-store, must-revalidate"; + add_header Pragma "no-cache"; + add_header Expires "0"; + } + } + + # Error pages + error_page 404 /index.html; + error_page 500 502 503 504 /50x.html; + + location = /50x.html { + root /usr/share/nginx/html; + } + } +} \ No newline at end of file