POST /api/analyze submits an analysis job and returns job_id instantly.
GET /api/analyze/{job_id} returns progress + result; frontend polls every 2s.
Analysis runs as asyncio.create_task in the background — each HTTP request
completes in milliseconds, well within the 30s GCP Load Balancer limit.
- Add backend/app/services/job_store.py: in-memory AnalysisJob store with
30-min TTL cleanup
- Add backend/app/api/analysis_routes.py: POST + GET /api/analyze endpoints
with full analysis pipeline (hash check, DB persistence, PDF pages, etc.)
- Remove backend/app/websocket/: handlers.py, manager.py, __init__.py
- Update backend/app/main.py: wire analysis_router, store analysis_service
in app.state, drop all WebSocket imports and endpoint
- Update frontend/services/geminiService.ts: replace WS with fetch+poll;
function signatures unchanged so App.tsx / WIPReviewer.tsx need no edits
- Remove VITE_BACKEND_WS_URL from vite.config.ts, deploy.sh, .env.deploy.example
- Update cloudrun.yaml: remove WebSocket-specific session affinity annotation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
217 lines
6 KiB
Bash
Executable file
217 lines
6 KiB
Bash
Executable file
#!/bin/bash
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
cd "$SCRIPT_DIR"
|
|
|
|
echo "=== ModComms Deployment ==="
|
|
echo "Working directory: $SCRIPT_DIR"
|
|
|
|
# --- Check prerequisites ---
|
|
echo ""
|
|
echo "[Prerequisites] Checking required tools..."
|
|
|
|
check_command() {
|
|
if ! command -v "$1" &> /dev/null; then
|
|
echo "Error: $1 is not installed"
|
|
exit 1
|
|
fi
|
|
echo " ✓ $1"
|
|
}
|
|
|
|
check_command git
|
|
check_command node
|
|
check_command npm
|
|
check_command docker
|
|
|
|
# --- Load deployment configuration ---
|
|
if [ -f .env.deploy ]; then
|
|
source .env.deploy
|
|
echo " ✓ .env.deploy loaded"
|
|
else
|
|
echo ""
|
|
echo "Error: .env.deploy not found"
|
|
echo "Create it from the template:"
|
|
echo " cp .env.deploy.example .env.deploy"
|
|
echo " nano .env.deploy"
|
|
exit 1
|
|
fi
|
|
|
|
# Validate required variables
|
|
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}"
|
|
}
|
|
|
|
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_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})"
|
|
|
|
# --- Generate .env (docker compose) ---
|
|
cat > .env << EOF
|
|
# 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}
|
|
POSTGRES_PORT=${POSTGRES_PORT}
|
|
POSTGRES_USER=${POSTGRES_USER}
|
|
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
|
POSTGRES_DB=${POSTGRES_DB}
|
|
EOF
|
|
echo " ✓ .env created for docker compose"
|
|
|
|
# --- 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"
|
|
|
|
# --- 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_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"
|
|
|
|
# --- 1. Pull latest code ---
|
|
echo ""
|
|
echo "[1/6] Updating code..."
|
|
if [ -d .git ]; then
|
|
if git remote -v | grep -q origin; then
|
|
git checkout -- frontend/package-lock.json 2>/dev/null || true
|
|
git pull || echo "Warning: git pull failed, continuing with local code"
|
|
else
|
|
echo " No remote configured, skipping git pull"
|
|
fi
|
|
else
|
|
echo " Not a git repository, skipping git pull"
|
|
fi
|
|
|
|
# --- 2. Build frontend ---
|
|
echo ""
|
|
echo "[2/6] Building frontend..."
|
|
cd frontend
|
|
npm install
|
|
npm run build
|
|
cd "$SCRIPT_DIR"
|
|
|
|
# --- 3. Deploy frontend to Apache ---
|
|
echo ""
|
|
echo "[3/6] Deploying frontend to ${FRONTEND_DEPLOY_DIR}..."
|
|
sudo mkdir -p "$FRONTEND_DEPLOY_DIR"
|
|
if [ -n "$FRONTEND_DEPLOY_DIR" ] && [ -d "$FRONTEND_DEPLOY_DIR" ]; then
|
|
sudo find "$FRONTEND_DEPLOY_DIR" -mindepth 1 -delete 2>/dev/null || true
|
|
fi
|
|
sudo cp -r frontend/dist/* "$FRONTEND_DEPLOY_DIR/"
|
|
sudo chown -R www-data:www-data "$FRONTEND_DEPLOY_DIR"
|
|
echo " ✓ Frontend deployed"
|
|
|
|
# --- 4. Build containers and run database migrations ---
|
|
echo ""
|
|
echo "[4/6] Building containers..."
|
|
docker compose build
|
|
|
|
echo " Starting PostgreSQL..."
|
|
docker compose up -d postgres
|
|
|
|
echo " Waiting for PostgreSQL to be ready..."
|
|
for i in {1..30}; do
|
|
if docker compose exec -T postgres pg_isready -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" > /dev/null 2>&1; then
|
|
echo " ✓ PostgreSQL is ready"
|
|
break
|
|
fi
|
|
if [ $i -eq 30 ]; then
|
|
echo " Error: PostgreSQL failed to start"
|
|
docker compose logs postgres
|
|
exit 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
# --- 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
|
|
echo " Error: Database migrations failed"
|
|
exit 1
|
|
fi
|
|
|
|
# --- 6. Start backend service ---
|
|
echo ""
|
|
echo "[6/6] Starting backend service..."
|
|
docker compose up -d --force-recreate backend
|
|
|
|
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
|
|
echo " ✓ Backend is healthy"
|
|
break
|
|
fi
|
|
if [ $i -eq 30 ]; then
|
|
echo " Warning: Backend health check timed out"
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
# --- Summary ---
|
|
echo ""
|
|
echo "========================================="
|
|
echo " Deployment Complete!"
|
|
echo "========================================="
|
|
echo ""
|
|
echo "Environment: ${COMPOSE_PROJECT_NAME}"
|
|
echo "Frontend: ${FRONTEND_DEPLOY_DIR}"
|
|
echo "Backend: http://localhost:${BACKEND_PORT}"
|
|
echo ""
|
|
docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"
|
|
echo ""
|
|
echo "Health check:"
|
|
curl -s "http://localhost:${BACKEND_PORT}/health" && echo "" || echo "Warning: Backend not responding"
|