From da63629720d88354ea7d24c18ad0e35f050d53a8 Mon Sep 17 00:00:00 2001 From: Vadym Samoilenko Date: Fri, 6 Mar 2026 11:57:17 +0000 Subject: [PATCH] Auto-generate backend/.env and frontend/.env.local from .env.deploy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The deploy script now generates both env files automatically — no more manual file creation on the server. All secrets and VITE_ vars are centralised in .env.deploy (gitignored). Updated .env.deploy.example with all required variables and inline documentation. Co-Authored-By: Claude Sonnet 4.6 --- .env.deploy.example | 94 ++++++++++++++++++------- deploy.sh | 162 +++++++++++++++++++------------------------- 2 files changed, 137 insertions(+), 119 deletions(-) diff --git a/.env.deploy.example b/.env.deploy.example index bcdce83..8cc7b4b 100644 --- a/.env.deploy.example +++ b/.env.deploy.example @@ -1,47 +1,91 @@ # ModComms Deployment Configuration -# Copy this file to .env.deploy and update with your server values +# Copy this file to .env.deploy and fill in your values. +# .env.deploy is gitignored — safe to store secrets here. # -# For running prod and dev on the same server, clone the repo twice: -# /opt/modcomms-prod/ - production (main branch) -# /opt/modcomms-dev/ - development (develop branch) -# Then configure each .env.deploy with different values below. +# The deploy script auto-generates backend/.env and frontend/.env.local +# from the variables below. You do NOT need to create those files manually. -# Docker project name (creates unique container names) +# ----------------------------------------------------------------------- +# Docker / Compose +# ----------------------------------------------------------------------- + +# Unique name per instance (creates unique container names) # Examples: modcomms-prod, modcomms-dev COMPOSE_PROJECT_NAME=modcomms-prod -# Backend port (must be unique per instance) -# Examples: 8000 for prod, 8001 for dev +# Backend port (must be unique per instance on this host) BACKEND_PORT=8000 -# PostgreSQL port (must be unique per instance) -# Examples: 5432 for prod, 5433 for dev +# PostgreSQL port (must be unique per instance on this host) POSTGRES_PORT=5432 -# PostgreSQL credentials (change in production!) +# PostgreSQL credentials POSTGRES_USER=modcomms POSTGRES_PASSWORD=change_this_in_production POSTGRES_DB=modcomms -# Frontend deployment directory (Apache document root) -# Production: /var/vhosts/baic.oliver.solutions/htdocs/modcomms -# Dev/staging: /var/www/html/modcomms +# ----------------------------------------------------------------------- +# Apache / Frontend +# ----------------------------------------------------------------------- + +# Directory where the built frontend is served from FRONTEND_DEPLOY_DIR=/var/vhosts/baic.oliver.solutions/htdocs/modcomms -# CORS origins (should match your frontend domain) -# Multiple origins can be comma-separated +# Subpath the app is served under (trailing slash required). +# Use / if served from the domain root. +VITE_BASE_PATH=/modcomms/ + +# ----------------------------------------------------------------------- +# Backend URLs +# ----------------------------------------------------------------------- + +# HTTP base URL of the backend (no trailing slash) +VITE_BACKEND_URL=https://baic.oliver.solutions/back + +# WebSocket URL for the analysis endpoint +VITE_BACKEND_WS_URL=wss://baic.oliver.solutions/back/ws/analyze + +# CORS origins allowed by the backend (must match VITE_BACKEND_URL origin) CORS_ORIGINS=https://baic.oliver.solutions -# NOTE: Frontend environment variables (VITE_*) are NOT configured here. -# Create frontend/.env.local manually with: -# VITE_BASE_PATH=/modcomms/ -# VITE_BACKEND_URL=https://baic.oliver.solutions/back -# VITE_BACKEND_WS_URL=wss://baic.oliver.solutions/back/ws/analyze -# VITE_AZURE_CLIENT_ID=your_azure_client_id -# VITE_AZURE_TENANT_ID=your_azure_tenant_id -# VITE_AZURE_REDIRECT_URI=https://baic.oliver.solutions/modcomms/ +# ----------------------------------------------------------------------- +# Azure AD +# ----------------------------------------------------------------------- + +AZURE_TENANT_ID=your_azure_tenant_id +AZURE_CLIENT_ID=your_azure_client_id + +# URL Azure redirects back to after login/logout (must match App Registration) +VITE_AZURE_REDIRECT_URI=https://baic.oliver.solutions/modcomms/ + +# Set to true only for local dev without Azure AD +DISABLE_AUTH=false + +# ----------------------------------------------------------------------- +# AI / LLM APIs +# ----------------------------------------------------------------------- + +GEMINI_API_KEY=your_gemini_api_key +LLAMA_CLOUD_API_KEY=your_llama_cloud_api_key + +# ----------------------------------------------------------------------- +# Email (Mailgun) +# ----------------------------------------------------------------------- + +MAILGUN_API_URL=https://api.mailgun.net/v3/oliver.solutions/messages +MAILGUN_API_KEY=your_mailgun_api_key +MAILGUN_FROM=admin@oliver.solutions +SUPPORT_EMAIL=BAICsupport@oliver.agency + +# ----------------------------------------------------------------------- +# Apache proxy config (for reference — configure in Apache vhost, not here) +# ----------------------------------------------------------------------- # -# Apache proxy config (production, port 8000): # ProxyPass /back/ws/analyze ws://localhost:8000/ws/analyze # ProxyPass /back/ http://localhost:8000/ # ProxyPassReverse /back/ http://localhost:8000/ +# +# # SPA fallback (serve index.html for all non-file routes under /modcomms/) +# +# FallbackResource /modcomms/index.html +# diff --git a/deploy.sh b/deploy.sh index 5e1088a..575d7cd 100755 --- a/deploy.sh +++ b/deploy.sh @@ -38,23 +38,43 @@ else fi # Validate required variables -if [ -z "$FRONTEND_DEPLOY_DIR" ]; then - echo "Error: FRONTEND_DEPLOY_DIR not set in .env.deploy" - exit 1 -fi +require_var() { + local var_name="$1" + local value="${!var_name}" + if [ -z "$value" ] || [[ "$value" == *"your_"* ]]; then + echo "" + echo "Error: ${var_name} is not configured in .env.deploy" + exit 1 + fi + echo " ✓ ${var_name}" +} -# Set defaults for docker compose variables +echo "" +echo "[Config] Validating required variables..." +require_var FRONTEND_DEPLOY_DIR +require_var GEMINI_API_KEY +require_var AZURE_TENANT_ID +require_var AZURE_CLIENT_ID +require_var VITE_BACKEND_URL +require_var VITE_BACKEND_WS_URL +require_var VITE_AZURE_REDIRECT_URI + +# Set defaults COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-modcomms}" BACKEND_PORT="${BACKEND_PORT:-8000}" POSTGRES_PORT="${POSTGRES_PORT:-5432}" POSTGRES_USER="${POSTGRES_USER:-modcomms}" POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-modcomms_dev}" POSTGRES_DB="${POSTGRES_DB:-modcomms}" +VITE_BASE_PATH="${VITE_BASE_PATH:-/}" +DISABLE_AUTH="${DISABLE_AUTH:-false}" +CORS_ORIGINS="${CORS_ORIGINS:-}" + echo " Environment: ${COMPOSE_PROJECT_NAME} (backend:${BACKEND_PORT}, postgres:${POSTGRES_PORT})" -# Create .env file for docker compose (so manual docker compose commands work) +# --- Generate .env (docker compose) --- cat > .env << EOF -# Auto-generated by deploy.sh - do not edit manually +# Auto-generated by deploy.sh — do not edit manually # Edit .env.deploy instead and re-run deploy.sh COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME} BACKEND_PORT=${BACKEND_PORT} @@ -65,47 +85,47 @@ POSTGRES_DB=${POSTGRES_DB} EOF echo " ✓ .env created for docker compose" -# --- Check backend/.env exists (required for GEMINI_API_KEY) --- -if [ ! -f backend/.env ]; then - if [ -f backend/.env.example ]; then - echo "" - echo "Error: backend/.env not found" - echo "Create it from the template:" - echo " cp backend/.env.example backend/.env" - echo " nano backend/.env # Add your GEMINI_API_KEY" - exit 1 - else - echo "Error: backend/.env not found and no template available" - exit 1 - fi -fi -echo " ✓ backend/.env exists" +# --- Generate backend/.env --- +echo "" +echo "[Config] Generating backend/.env..." +cat > backend/.env << EOF +# Auto-generated by deploy.sh — do not edit manually +# Edit .env.deploy instead and re-run deploy.sh +GEMINI_API_KEY=${GEMINI_API_KEY} +CORS_ORIGINS=${CORS_ORIGINS} +HOST=0.0.0.0 +PORT=${BACKEND_PORT} +AZURE_TENANT_ID=${AZURE_TENANT_ID} +AZURE_CLIENT_ID=${AZURE_CLIENT_ID} +DISABLE_AUTH=${DISABLE_AUTH} +MAILGUN_API_URL=${MAILGUN_API_URL} +MAILGUN_API_KEY=${MAILGUN_API_KEY} +MAILGUN_FROM=${MAILGUN_FROM} +SUPPORT_EMAIL=${SUPPORT_EMAIL} +LLAMA_CLOUD_API_KEY=${LLAMA_CLOUD_API_KEY} +EOF +echo " ✓ backend/.env generated" -# Validate that critical backend env vars are actually set (not placeholders) -check_backend_env() { - local var_name="$1" - local value - value=$(grep -E "^${var_name}=" backend/.env | cut -d= -f2- | tr -d '"' | tr -d "'") - if [ -z "$value" ] || [[ "$value" == *"your_"* ]]; then - echo "" - echo "Error: ${var_name} is not configured in backend/.env" - echo " Open backend/.env and set a real value for ${var_name}" - exit 1 - fi - echo " ✓ ${var_name} is set" -} +# --- Generate frontend/.env.local --- +echo "" +echo "[Config] Generating frontend/.env.local..." +cat > frontend/.env.local << EOF +# Auto-generated by deploy.sh — do not edit manually +# Edit .env.deploy instead and re-run deploy.sh +VITE_BASE_PATH=${VITE_BASE_PATH} +VITE_BACKEND_URL=${VITE_BACKEND_URL} +VITE_BACKEND_WS_URL=${VITE_BACKEND_WS_URL} +VITE_AZURE_CLIENT_ID=${AZURE_CLIENT_ID} +VITE_AZURE_TENANT_ID=${AZURE_TENANT_ID} +VITE_AZURE_REDIRECT_URI=${VITE_AZURE_REDIRECT_URI} +EOF +echo " ✓ frontend/.env.local generated" -check_backend_env GEMINI_API_KEY -check_backend_env AZURE_TENANT_ID -check_backend_env AZURE_CLIENT_ID -# DATABASE_URL is intentionally omitted here — it is injected by docker-compose.yml - -# --- 1. Pull latest code (skip if not a git repo or no remote) --- +# --- 1. Pull latest code --- echo "" echo "[1/6] Updating code..." if [ -d .git ]; then if git remote -v | grep -q origin; then - # Discard local changes to package-lock.json (regenerated by npm install anyway) git checkout -- frontend/package-lock.json 2>/dev/null || true git pull || echo "Warning: git pull failed, continuing with local code" else @@ -119,70 +139,26 @@ fi echo "" echo "[2/6] Building frontend..." cd frontend - -# Install dependencies (npm install is idempotent) npm install - -# Check that .env.local exists (must be created manually with credentials) -if [ ! -f .env.local ]; then - echo "" - echo "Error: frontend/.env.local not found" - echo "Create it manually with your environment-specific values:" - echo "" - echo " cat > frontend/.env.local << 'EOF'" - echo " VITE_BACKEND_WS_URL=wss://your-domain.com/ws/analyze" - echo " VITE_BACKEND_URL=https://your-domain.com" - echo " VITE_AZURE_CLIENT_ID=your_azure_client_id" - echo " VITE_AZURE_TENANT_ID=your_azure_tenant_id" - echo " VITE_AZURE_REDIRECT_URI=https://your-domain.com" - echo " EOF" - echo "" - exit 1 -fi -echo " ✓ .env.local exists" - npm run build cd "$SCRIPT_DIR" # --- 3. Deploy frontend to Apache --- echo "" echo "[3/6] Deploying frontend to ${FRONTEND_DEPLOY_DIR}..." - -# Create directory if it doesn't exist sudo mkdir -p "$FRONTEND_DEPLOY_DIR" - -# Clear existing files (safe deletion with variable check) if [ -n "$FRONTEND_DEPLOY_DIR" ] && [ -d "$FRONTEND_DEPLOY_DIR" ]; then sudo find "$FRONTEND_DEPLOY_DIR" -mindepth 1 -delete 2>/dev/null || true fi - -# Copy new build sudo cp -r frontend/dist/* "$FRONTEND_DEPLOY_DIR/" sudo chown -R www-data:www-data "$FRONTEND_DEPLOY_DIR" echo " ✓ Frontend deployed" -# --- 4. Update backend configuration --- +# --- 4. Build containers and run database migrations --- echo "" -echo "[4/6] Updating backend configuration..." - -# Update CORS_ORIGINS if specified -if [ -n "$CORS_ORIGINS" ]; then - if grep -q "^CORS_ORIGINS=" backend/.env; then - sed -i "s|^CORS_ORIGINS=.*|CORS_ORIGINS=${CORS_ORIGINS}|" backend/.env - else - echo "CORS_ORIGINS=${CORS_ORIGINS}" >> backend/.env - fi - echo " ✓ CORS_ORIGINS updated" -fi - -# --- 5. Build containers and run database migrations --- -echo "" -echo "[5/6] Building containers and running database migrations..." - -# Build image (always rebuild to pick up code changes) +echo "[4/6] Building containers..." docker compose build -# Start PostgreSQL first and wait for it to be healthy echo " Starting PostgreSQL..." docker compose up -d postgres @@ -200,8 +176,9 @@ for i in {1..30}; do sleep 1 done -# Run database migrations -echo " Running database migrations..." +# --- 5. Run database migrations --- +echo "" +echo "[5/6] Running database migrations..." if docker compose run --rm backend alembic upgrade head; then echo " ✓ Database migrations complete" else @@ -212,11 +189,8 @@ fi # --- 6. Start backend service --- echo "" echo "[6/6] Starting backend service..." - -# Start backend container (force recreate to ensure new image is used) docker compose up -d --force-recreate backend -# Wait for health check echo " Waiting for backend to be healthy..." for i in {1..30}; do if curl -sf "http://localhost:${BACKEND_PORT}/health" > /dev/null 2>&1; then @@ -229,7 +203,7 @@ for i in {1..30}; do sleep 1 done -# --- Deployment summary --- +# --- Summary --- echo "" echo "=========================================" echo " Deployment Complete!"