# ShopDB Docker Setup Complete Docker deployment for development and testing. ## Prerequisites - Docker Desktop installed and running - At least 8GB RAM allocated to Docker - Zscaler corporate certificate extracted (if behind corporate proxy) ## Quick Start ```bash # 1. Clone repository cd C:/Users/YOUR_USERNAME/Documents/github/flask-shopdb # 2. Extract Zscaler certificate (if needed) powershell.exe -File C:/temp/extract-ca.ps1 # This creates corp-root-ca.pem in repo root # 3. Build images docker compose build # 4. Start stack docker compose up -d # 5. Initialize database (first time only) docker compose exec api flask db upgrade # 6. Install plugins (first time only) docker compose exec api flask plugin apply-profile deploy/site-profile-universal.json # 7. Access ShopDB https://localhost:5001 ``` Login: `admin` / (reset password - see below) ## Architecture The Docker stack includes: 1. **MySQL Database** (`db` service) - Port: 3306 (internal), mapped to host based on `.env` - Volume: `db_data` for persistence - Health checks enabled 2. **Flask API** (`api` service) - Port: 5001 (HTTPS with self-signed cert) - Built frontend served from `/app/frontend/dist` - Gunicorn with gevent workers (4 workers, 2000 connections each) - Volumes: - `instance_data` - Plugin state, uploads, branding - `./plugins` - Plugin code (read-only) - `./scripts` - Deployment scripts (read-only) - `./certs` - TLS certificates (read-only) 3. **Backup Service** (optional, via `docker-compose.backup.yml`) - One-shot container for backups - Triggered manually or via scheduler ## First-Time Setup ### 1. Extract Zscaler Certificate (Corporate Proxy) Save this as `C:/temp/extract-ca.ps1`: ```powershell $request = [System.Net.HttpWebRequest]::Create('https://registry.npmjs.org/') $request.Method = 'HEAD' $request.Timeout = 10000 $response = $request.GetResponse() $root = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($request.ServicePoint.Certificate) $builder = [System.Text.StringBuilder]::new() [void]$builder.AppendLine('-----BEGIN CERTIFICATE-----') [void]$builder.AppendLine([System.Convert]::ToBase64String($root.RawData, 'InsertLineBreaks')) [void]$builder.AppendLine('-----END CERTIFICATE-----') $certPath = 'C:\Users\270015376\Documents\github\flask-shopdb\corp-root-ca.pem' Set-Content -Path $certPath -Value $builder.ToString() -Encoding ASCII Write-Host "Staged corp-root-ca.pem" ``` Run: `powershell.exe -ExecutionPolicy Bypass -File C:/temp/extract-ca.ps1` ### 2. Generate TLS Certificates ```bash cd certs openssl req -x509 -newkey rsa:2048 -nodes \ -keyout server.key -out server.crt -days 365 \ -subj "//CN=localhost" ``` Or use the provided script: ```bash ./scripts/generate-dev-certs.sh ``` ### 3. Configure Environment Copy and edit `.env`: ```bash cp .env.docker-dev .env # Edit if needed - defaults work for local dev ``` ### 4. Build Images ```bash docker compose build ``` **Note:** First build takes 10-15 minutes due to: - npm install (~5 min with Zscaler proxy) - Frontend Vite build (~2 min) - Python dependencies (~3 min) Subsequent builds use Docker layer cache and are much faster. ### 5. Start Services ```bash docker compose up -d ``` Check status: ```bash docker compose ps docker compose logs -f api ``` ### 6. Initialize Database ```bash # Run migrations docker compose exec api flask db upgrade # Install and enable all plugins docker compose exec api flask plugin apply-profile deploy/site-profile-universal.json ``` The `apply-profile` command: - Installs all 17 plugins - Registers them in `instance/plugins.json` - Leaves them disabled by default - Enables them via Settings UI as needed ### 7. Reset Admin Password ```bash # Generate hash for "admin" password docker compose exec api python -c "from werkzeug.security import generate_password_hash; print(generate_password_hash('admin'))" # Update database docker compose exec db mysql -u root -p shopdb_flask -e " UPDATE users SET passwordhash = 'PASTE_HASH_HERE', failedlogins = 0, lockeduntil = NULL WHERE username = 'admin';" ``` Or use quick method (sets password to `admin`): ```bash docker compose exec db mysql -u root -p shopdb_flask -e " UPDATE users SET passwordhash = 'scrypt:32768:8:1\$B4LhpPrM6qxijInK\$b99b47a0bb5fd45761a1823f401f70a3ceccf71a5d38dc433378aa07426ef8eaf7e679ff3dd7b0ce9772600f83368ac642e9419b9beaa49f87483318bae34547', failedlogins = 0, lockeduntil = NULL WHERE username = 'admin';" ``` ## Import Production Database ```bash # Copy SQL dump to container docker cp latest.sql shopdb-api-1:/tmp/ # Import docker compose exec db mysql -u root -p shopdb_flask < /tmp/latest.sql # Run migrations to bring schema up to date docker compose exec api flask db upgrade docker compose exec api flask plugin upgrade-all ``` ## Common Commands ### View Logs ```bash docker compose logs -f api docker compose logs -f db ``` ### Restart Services ```bash docker compose restart api docker compose down && docker compose up -d ``` ### Rebuild After Code Changes ```bash docker compose build api docker compose up -d api ``` ### Access Container Shell ```bash docker compose exec api bash docker compose exec db bash ``` ### Run Flask Commands ```bash docker compose exec api flask plugin list docker compose exec api flask db current docker compose exec api flask routes ``` ### Backup & Restore ```bash # Trigger backup docker compose -f docker-compose.backup.yml up backup # View backups docker compose exec api flask plugin list # (Accessible via Settings > Backups in UI) # Restore (via UI at Settings > Backups > Select backup > Restore) ``` ## Troubleshooting ### "npm ci failed" during build **Cause:** Zscaler proxy blocking npm registry. **Fix:** 1. Extract Zscaler certificate (see above) 2. Ensure `corp-root-ca.pem` exists in repo root 3. Rebuild: `docker compose build --no-cache` ### "database is unhealthy" **Cause:** MySQL container not ready or port conflict. **Fix:** ```bash # Check logs docker compose logs db # Check if port 3306 is already in use netstat -an | grep 3306 # If conflict, edit .env and set MYSQL_PORT=3307 ``` ### "Access denied for user 'shopdb'" **Cause:** Database credentials mismatch. **Fix:** Check `.env` file - `MYSQL_PASSWORD` must match `DATABASE_URL`. ### "Could not connect to database" **Cause:** Database service not started or wrong hostname. **Fix:** ```bash # Check db is running docker compose ps # API must use hostname 'db' not 'localhost' # In docker-compose.yml: DATABASE_URL=...@db:3306/... ``` ### "ERR_CERT_AUTHORITY_INVALID" in browser **Expected:** Self-signed certificate warning. **Fix:** Click "Advanced" > "Proceed to localhost (unsafe)" - this is normal for dev. Or generate proper cert: ```bash # Add your machine's hostname to cert openssl req -x509 -newkey rsa:2048 -nodes \ -keyout certs/server.key -out certs/server.crt -days 365 \ -subj "//CN=$(hostname)" \ -addext "subjectAltName=DNS:localhost,DNS:$(hostname),IP:127.0.0.1" ``` ### "Plugin X is already installed" but not visible **Cause:** Plugin installed but not enabled. **Fix:** ```bash docker compose exec api flask plugin enable X docker compose restart api ``` ### Build is very slow **Expected:** First build with Zscaler proxy takes 10-15 minutes. **Speed up:** - Subsequent builds use cache (much faster) - If rebuilding often, comment out `--no-cache` flag - Build frontend separately on host, copy dist/ into image ## Production Deployment Notes For production: 1. **Change all secrets** in `.env`: - `SECRET_KEY` - `JWT_SECRET_KEY` - `MYSQL_ROOT_PASSWORD` - `MYSQL_PASSWORD` 2. **Use proper TLS certificates** (not self-signed): - Place in `./certs/server.crt` and `./certs/server.key` - Or use reverse proxy (nginx/IIS) for TLS termination 3. **Set `FLASK_DEBUG=0`** in production 4. **Configure backups**: - Set up cron job: `0 2 * * * cd /path/to/shopdb && docker compose -f docker-compose.backup.yml up backup` - Configure SMB or S3 destination in Settings > Backups 5. **Set `SHOPDB_ENV_LABEL`** to site name (e.g., "WEST-JEFFERSON") 6. **Review `docker-compose.yml`**: - Adjust worker count based on load - Configure proper `restart` policy - Set resource limits if needed ## Architecture Notes ### Why HTTPS in Docker? Secure context APIs (clipboard, geolocation) require HTTPS. Running over HTTP disables these browser features. ### Why Gevent Workers? Endpoint agents long-poll `/api/endpoints/commands/next`. With threading, 200 agents = 200 threads = resource exhaustion. Gevent uses greenlets (cheap coroutines) so 2000+ connections per worker is feasible. ### Plugin Bootstrap The entrypoint (`scripts/docker-entrypoint.sh`) automatically: 1. Runs database migrations 2. Checks if `instance/plugins.json` exists 3. If not, installs all available plugins (disabled) 4. Starts gunicorn This ensures imported databases with plugin tables get matching plugin registry. ### Instance Data Persistence The `instance_data` volume persists: - `plugins.json` - Which plugins are enabled - Uploaded files (floor plans, branding, photos, warranties, slides) - Dell OAuth tokens - Any plugin-specific state **Important:** `docker compose down -v` deletes volumes - use plain `docker compose down` to preserve data. ## File Structure ``` flask-shopdb/ |-- docker-compose.yml # Main stack definition |-- docker-compose.backup.yml # Backup service |-- Dockerfile # Multi-stage build |-- .env # Environment config (gitignored) |-- corp-root-ca.pem # Zscaler cert (gitignored) |-- certs/ # TLS certificates (gitignored) | |-- server.crt | +-- server.key |-- scripts/ | |-- docker-entrypoint.sh # Container startup | +-- bootstrap-plugins.sh # Plugin auto-install +-- deploy/ +-- site-profile-universal.json # Full plugin list ``` ## Next Steps After setup: 1. Browse to https://localhost:5001 2. Login as `admin` 3. Go to Settings > Plugins 4. Enable plugins your site needs 5. Configure settings (CORS, backup destination, etc.) 6. Import data or start fresh For local development with hot-reload, see `LOCAL-DEV-SETUP.md`.