diff --git a/DEPLOY-TO-VM.md b/DEPLOY-TO-VM.md new file mode 100644 index 00000000..17002a42 --- /dev/null +++ b/DEPLOY-TO-VM.md @@ -0,0 +1,177 @@ +# Deploy ShopDB to Hyper-V VM + +Quick guide to deploy the Docker stack to a Windows 11 Hyper-V VM. + +## Prerequisites on VM + +1. **Docker Desktop** installed and running +2. **Git Bash** or **WSL** installed (for bash commands) +3. **PowerShell** (already on Windows 11) + +## Step 1: Copy Files to VM + +**Method A: Shared Folder** +```powershell +# On host PC, share the repo +# Then on VM, copy from network share +Copy-Item -Recurse \\host-pc\shopdb-flask C:\shopdb +``` + +**Method B: Git Clone** +```bash +# On VM +cd C:\ +git clone git@github.com:ge-aero/shopdb-flask.git +cd shopdb-flask +git checkout wj +``` + +**Method C: Export/Import** +```powershell +# On host PC +Compress-Archive -Path C:\Users\270015376\Documents\github\flask-shopdb -DestinationPath C:\shopdb-deploy.zip + +# Copy shopdb-deploy.zip to VM, then on VM: +Expand-Archive -Path C:\shopdb-deploy.zip -DestinationPath C:\shopdb +``` + +## Step 2: Run Deployment Script + +```powershell +# On VM, open PowerShell as Administrator +cd C:\shopdb + +# Option 1: Quick deploy with defaults +.\deploy-to-vm.ps1 + +# Option 2: Specify site name and password +.\deploy-to-vm.ps1 -SiteName "west-jefferson" -AdminPassword "YourSecurePassword123!" + +# Option 3: See all options +.\deploy-to-vm.ps1 -Help +``` + +## What the Script Does + +1. ✅ Checks Docker Desktop is running +2. ✅ Creates .env from template +3. ✅ Builds frontend (if needed) +4. ✅ Builds Docker images (2-3 minutes, offline packages) +5. ✅ Starts all services (db + api + backup) +6. ✅ Runs database migrations +7. ✅ Sets admin password + +## Manual Deployment (Alternative) + +If you prefer to run commands manually: + +```bash +# 1. Create environment +cp .env.docker-dev .env + +# 2. Build frontend (if not already built) +cd frontend +npm ci +npm run build +cd .. + +# 3. Build and start Docker stack +docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml --profile backup build +docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml --profile backup up -d + +# 4. Initialize database +docker exec flask-shopdb-api-1 flask db upgrade + +# 5. Reset admin password (optional) +docker exec flask-shopdb-db-1 mysql -uroot -proot-dev-password-not-for-production shopdb_flask -e \ + "UPDATE users SET passwordhash='scrypt:32768:8:1\$B4LhpPrM6qxijInK\$b99b47a0bb5fd45761a1823f401f70a3ceccf71a5d38dc433378aa07426ef8eaf7e679ff3dd7b0ce9772600f83368ac642e9419b9beaa49f87483318bae34547', failedlogins=0, lockeduntil=NULL WHERE username='admin';" +# (Password is: admin) +``` + +## Access + +**URL:** https://localhost:5001 (or https://vm-ip-address:5001 from host) + +**Login:** +- Username: `admin` +- Password: Whatever you set in deploy script (default: `ChangeMe123!`) + +## Verify Deployment + +```powershell +# Check all services are running +docker ps + +# Should see: +# - flask-shopdb-db-1 +# - flask-shopdb-api-1 +# - flask-shopdb-backup-store-1 +# - flask-shopdb-backup-watcher-1 + +# View logs +docker logs -f flask-shopdb-api-1 + +# Test backup +# Go to https://localhost:5001/settings/shopdbbackups +# Click "Back Up Now" +# Wait 30 seconds, should show as "Checked" +``` + +## Troubleshooting + +**Docker not running:** +```powershell +# Start Docker Desktop manually +& "C:\Program Files\Docker\Docker\Docker Desktop.exe" +Start-Sleep -Seconds 30 +``` + +**Port 5001 already in use:** +```powershell +# Edit .env and change API_PORT +# API_PORT=5002 +``` + +**Can't access from host PC:** +```powershell +# On VM, check Windows Firewall +New-NetFirewallRule -DisplayName "ShopDB" -Direction Inbound -LocalPort 5001 -Protocol TCP -Action Allow +``` + +**SSL certificate warnings:** +- Normal for self-signed certs +- Click "Advanced" → "Proceed to localhost (unsafe)" + +## Stop/Start + +```bash +# Stop +docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml down + +# Start (after first deployment) +docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml --profile backup up -d +``` + +## Update After Code Changes + +```bash +# 1. Pull latest code +git pull origin wj + +# 2. Rebuild frontend (if changed) +cd frontend && npm run build && cd .. + +# 3. Rebuild and restart +docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml build +docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml up -d +``` + +## Offline Deployment Notes + +**This deployment works completely offline** because: +- ✅ Frontend pre-built and checked into repo +- ✅ Restic binary pre-downloaded (8.6MB in repo) +- ✅ No npm/pip downloads during Docker build +- ✅ All dependencies baked into images + +Perfect for air-gapped or restricted network environments! diff --git a/deploy-to-vm.ps1 b/deploy-to-vm.ps1 new file mode 100644 index 00000000..a5c2f902 --- /dev/null +++ b/deploy-to-vm.ps1 @@ -0,0 +1,136 @@ +# ShopDB Docker Deployment Script for Hyper-V VM +# Run this ON THE VM after copying the repo + +param( + [string]$SiteName = "shopdb-vm", + [string]$AdminPassword = "ChangeMe123!", + [switch]$Help +) + +if ($Help) { + Write-Host @" +ShopDB Docker Deployment Script + +Usage: + .\deploy-to-vm.ps1 [-SiteName ] [-AdminPassword ] + +Parameters: + -SiteName Site identifier (default: shopdb-vm) + -AdminPassword Initial admin password (default: ChangeMe123!) + -Help Show this help + +Example: + .\deploy-to-vm.ps1 -SiteName "west-jefferson" -AdminPassword "SecurePass123!" + +Prerequisites: + - Docker Desktop installed and running + - Git Bash or WSL available +"@ + exit 0 +} + +$ErrorActionPreference = "Stop" + +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "ShopDB Docker Deployment" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" + +# Check Docker is running +Write-Host "[1/8] Checking Docker Desktop..." -ForegroundColor Yellow +try { + docker ps | Out-Null + Write-Host " OK: Docker is running" -ForegroundColor Green +} catch { + Write-Host " ERROR: Docker Desktop is not running" -ForegroundColor Red + Write-Host " Please start Docker Desktop and try again" -ForegroundColor Red + exit 1 +} + +# Check if we're in the repo +Write-Host "[2/8] Checking repository..." -ForegroundColor Yellow +if (-not (Test-Path "docker-compose.prebuilt.yml")) { + Write-Host " ERROR: Not in shopdb-flask directory" -ForegroundColor Red + Write-Host " Please cd to the repository root" -ForegroundColor Red + exit 1 +} +Write-Host " OK: In repository root" -ForegroundColor Green + +# Create .env if it doesn't exist +Write-Host "[3/8] Configuring environment..." -ForegroundColor Yellow +if (-not (Test-Path ".env")) { + Copy-Item ".env.docker-dev" ".env" + Write-Host " Created .env from template" -ForegroundColor Green + + # Update site name + (Get-Content ".env") -replace 'SHOPDB_SITE=shopdb-dev', "SHOPDB_SITE=$SiteName" | Set-Content ".env" + (Get-Content ".env") -replace 'SHOPDB_ENV_LABEL=DOCKER-DEV', "SHOPDB_ENV_LABEL=$SiteName" | Set-Content ".env" + Write-Host " Configured for site: $SiteName" -ForegroundColor Green +} else { + Write-Host " OK: .env already exists" -ForegroundColor Green +} + +# Build frontend if needed +Write-Host "[4/8] Checking frontend build..." -ForegroundColor Yellow +if (-not (Test-Path "frontend/dist/index.html")) { + Write-Host " Building frontend (this may take 2-3 minutes)..." -ForegroundColor Yellow + Set-Location frontend + npm ci + npm run build + Set-Location .. + Write-Host " OK: Frontend built" -ForegroundColor Green +} else { + Write-Host " OK: Frontend already built" -ForegroundColor Green +} + +# Build Docker images +Write-Host "[5/8] Building Docker images..." -ForegroundColor Yellow +Write-Host " This will take 2-3 minutes (using offline packages)..." -ForegroundColor Yellow +docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml build +Write-Host " OK: Images built" -ForegroundColor Green + +# Start services +Write-Host "[6/8] Starting services..." -ForegroundColor Yellow +docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml --profile backup up -d +Start-Sleep -Seconds 10 +Write-Host " OK: Services started" -ForegroundColor Green + +# Run migrations +Write-Host "[7/8] Initializing database..." -ForegroundColor Yellow +docker exec flask-shopdb-api-1 flask db upgrade +Write-Host " OK: Database initialized" -ForegroundColor Green + +# Set admin password +Write-Host "[8/8] Setting admin password..." -ForegroundColor Yellow +$passwordHash = docker exec flask-shopdb-api-1 python -c "from werkzeug.security import generate_password_hash; print(generate_password_hash('$AdminPassword'))" +$dbPassword = (Get-Content ".env" | Select-String "MYSQL_ROOT_PASSWORD=(.+)" | ForEach-Object { $_.Matches.Groups[1].Value }) + +docker exec flask-shopdb-db-1 mysql -uroot -p$dbPassword shopdb_flask -e @" +UPDATE users SET + passwordhash = '$passwordHash', + failedlogins = 0, + lockeduntil = NULL +WHERE username = 'admin'; +"@ +Write-Host " OK: Admin password set" -ForegroundColor Green + +Write-Host "" +Write-Host "========================================" -ForegroundColor Green +Write-Host "Deployment Complete!" -ForegroundColor Green +Write-Host "========================================" -ForegroundColor Green +Write-Host "" +Write-Host "Access ShopDB at: https://localhost:5001" -ForegroundColor Cyan +Write-Host "" +Write-Host "Login credentials:" -ForegroundColor Yellow +Write-Host " Username: admin" -ForegroundColor White +Write-Host " Password: $AdminPassword" -ForegroundColor White +Write-Host "" +Write-Host "Services running:" -ForegroundColor Yellow +docker ps --format "table {{.Names}}\t{{.Status}}" +Write-Host "" +Write-Host "To view logs:" -ForegroundColor Yellow +Write-Host " docker logs -f flask-shopdb-api-1" -ForegroundColor White +Write-Host "" +Write-Host "To stop:" -ForegroundColor Yellow +Write-Host " docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml down" -ForegroundColor White +Write-Host ""