1002 lines
32 KiB
Bash
1002 lines
32 KiB
Bash
#!/bin/bash
|
|
#
|
|
# IT0 Backend Services - Deployment Script
|
|
# =========================================
|
|
#
|
|
# Usage:
|
|
# ./deploy.sh install # First time setup (generate secrets, init databases)
|
|
# ./deploy.sh up # Start all services
|
|
# ./deploy.sh down # Stop all services
|
|
# ./deploy.sh restart # Restart all services
|
|
# ./deploy.sh status # Show service status
|
|
# ./deploy.sh logs [svc] # View logs (optional: specific service)
|
|
# ./deploy.sh build # Rebuild all images
|
|
# ./deploy.sh migrate # Run database migrations (TypeORM)
|
|
# ./deploy.sh health # Check health of all services
|
|
#
|
|
# Infrastructure:
|
|
# ./deploy.sh infra-up # Start only infrastructure (postgres, redis)
|
|
# ./deploy.sh infra-down # Stop infrastructure
|
|
# ./deploy.sh infra-restart # Restart infrastructure
|
|
# ./deploy.sh infra-status # Show infrastructure status
|
|
# ./deploy.sh infra-logs # View infrastructure logs
|
|
#
|
|
# Voice Service (GPU):
|
|
# ./deploy.sh voice-up # Start voice service with GPU support
|
|
# ./deploy.sh voice-down # Stop voice service
|
|
# ./deploy.sh voice-logs # View voice service logs
|
|
#
|
|
# SSL (Let's Encrypt):
|
|
# ./deploy.sh ssl-init # Obtain SSL certificates for both domains
|
|
# ./deploy.sh ssl-up # Start with Nginx + SSL
|
|
# ./deploy.sh ssl-renew # Manually renew certificates
|
|
# ./deploy.sh ssl-status # Check certificate status
|
|
#
|
|
|
|
set -e
|
|
|
|
# ===========================================================================
|
|
# Configuration
|
|
# ===========================================================================
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
ENV_FILE="$SCRIPT_DIR/.env"
|
|
COMPOSE_FILE="$SCRIPT_DIR/docker-compose.yml"
|
|
COMPOSE_VOICE_FILE="$SCRIPT_DIR/docker-compose.voice.yml"
|
|
COMPOSE_SSL_FILE="$SCRIPT_DIR/docker-compose.ssl.yml"
|
|
|
|
# Domain configuration
|
|
API_DOMAIN="${API_DOMAIN:-it0api.szaiai.com}"
|
|
WEB_DOMAIN="${WEB_DOMAIN:-it0.szaiai.com}"
|
|
CERT_EMAIL="${CERT_EMAIL:-admin@szaiai.com}"
|
|
|
|
# Container name prefix
|
|
CONTAINER_PREFIX="it0"
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
log_step() { echo -e "${BLUE}[STEP]${NC} $1"; }
|
|
|
|
# ===========================================================================
|
|
# Helper Functions
|
|
# ===========================================================================
|
|
|
|
generate_random_password() {
|
|
openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32
|
|
}
|
|
|
|
generate_hex_key() {
|
|
openssl rand -hex 32
|
|
}
|
|
|
|
check_docker() {
|
|
if ! command -v docker &> /dev/null; then
|
|
log_error "Docker is not installed. Please install Docker first."
|
|
exit 1
|
|
fi
|
|
|
|
if ! docker compose version &> /dev/null; then
|
|
log_error "Docker Compose is not installed. Please install Docker Compose first."
|
|
exit 1
|
|
fi
|
|
|
|
log_info "Docker version: $(docker --version)"
|
|
log_info "Docker Compose version: $(docker compose version --short)"
|
|
}
|
|
|
|
# ===========================================================================
|
|
# Install / Initialize
|
|
# ===========================================================================
|
|
|
|
install() {
|
|
log_step "Installing IT0 Backend Services..."
|
|
|
|
check_docker
|
|
|
|
# Generate .env file if not exists
|
|
if [ ! -f "$ENV_FILE" ]; then
|
|
log_step "Generating secure configuration..."
|
|
|
|
POSTGRES_PASSWORD=$(generate_random_password)
|
|
JWT_SECRET=$(generate_random_password)
|
|
JWT_REFRESH_SECRET=$(generate_random_password)
|
|
VAULT_MASTER_KEY=$(generate_hex_key)
|
|
|
|
cat > "$ENV_FILE" << EOF
|
|
# =============================================================================
|
|
# IT0 Backend Services - Production Environment Configuration
|
|
# =============================================================================
|
|
# Generated: $(date)
|
|
# WARNING: Keep this file secure! Do not commit to version control!
|
|
# =============================================================================
|
|
|
|
# PostgreSQL Database
|
|
POSTGRES_USER=it0
|
|
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
|
POSTGRES_DB=it0
|
|
|
|
# Redis (leave empty for no password)
|
|
REDIS_PASSWORD=
|
|
|
|
# JWT Configuration
|
|
JWT_SECRET=${JWT_SECRET}
|
|
JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET}
|
|
|
|
# Vault Master Key (for credential encryption in inventory-service)
|
|
VAULT_MASTER_KEY=${VAULT_MASTER_KEY}
|
|
|
|
# Anthropic API Key (for agent-service AI capabilities)
|
|
ANTHROPIC_API_KEY=
|
|
|
|
# Twilio (for comm-service SMS/voice calls, optional)
|
|
TWILIO_ACCOUNT_SID=
|
|
TWILIO_AUTH_TOKEN=
|
|
TWILIO_PHONE_NUMBER=
|
|
|
|
# Voice Service Configuration
|
|
WHISPER_MODEL=large-v3
|
|
KOKORO_MODEL=kokoro-82m
|
|
VOICE_DEVICE=cpu
|
|
EOF
|
|
|
|
chmod 600 "$ENV_FILE"
|
|
log_info "Environment file created: $ENV_FILE"
|
|
log_info "Secrets have been auto-generated"
|
|
log_warn "Please set ANTHROPIC_API_KEY in $ENV_FILE before starting services"
|
|
else
|
|
log_info "Environment file already exists: $ENV_FILE"
|
|
fi
|
|
|
|
# Create scripts directory
|
|
mkdir -p "$SCRIPT_DIR/scripts"
|
|
|
|
# Create database init script
|
|
create_db_init_script
|
|
|
|
log_info "Installation complete!"
|
|
log_info ""
|
|
log_info "Next steps:"
|
|
log_info " 1. Edit .env and set ANTHROPIC_API_KEY"
|
|
log_info " 2. Run: ./deploy.sh build"
|
|
log_info " 3. Run: ./deploy.sh up"
|
|
}
|
|
|
|
create_db_init_script() {
|
|
cat > "$SCRIPT_DIR/scripts/init-databases.sh" << 'DBSCRIPT'
|
|
#!/bin/bash
|
|
set -e
|
|
|
|
# IT0 uses schema-per-tenant with a single database
|
|
# This script ensures the main database exists and creates
|
|
# required schemas for multi-tenant isolation
|
|
|
|
echo "Initializing IT0 database..."
|
|
|
|
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
-- Create shared schema for cross-tenant data
|
|
CREATE SCHEMA IF NOT EXISTS shared;
|
|
|
|
-- Create default tenant schema
|
|
CREATE SCHEMA IF NOT EXISTS tenant_default;
|
|
|
|
-- Grant permissions
|
|
GRANT ALL ON SCHEMA shared TO $POSTGRES_USER;
|
|
GRANT ALL ON SCHEMA tenant_default TO $POSTGRES_USER;
|
|
EOSQL
|
|
|
|
echo "Database initialization complete!"
|
|
DBSCRIPT
|
|
|
|
chmod +x "$SCRIPT_DIR/scripts/init-databases.sh"
|
|
log_info "Database init script created"
|
|
}
|
|
|
|
# ===========================================================================
|
|
# Docker Compose Operations
|
|
# ===========================================================================
|
|
|
|
up() {
|
|
log_step "Starting IT0 Backend Services..."
|
|
|
|
if [ ! -f "$ENV_FILE" ]; then
|
|
log_error "Environment file not found. Run './deploy.sh install' first."
|
|
exit 1
|
|
fi
|
|
|
|
# Start infrastructure first
|
|
log_info "Starting infrastructure services..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d postgres redis
|
|
|
|
# Wait for infrastructure
|
|
log_info "Waiting for infrastructure to be ready..."
|
|
wait_for_postgres
|
|
wait_for_redis
|
|
|
|
# Start application services
|
|
log_info "Starting application services..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d
|
|
|
|
log_info "All services started!"
|
|
log_info ""
|
|
log_info "Check status with: ./deploy.sh status"
|
|
log_info "View logs with: ./deploy.sh logs"
|
|
}
|
|
|
|
down() {
|
|
log_step "Stopping IT0 Backend Services..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" down
|
|
log_info "All services stopped"
|
|
}
|
|
|
|
restart() {
|
|
log_step "Restarting IT0 Backend Services..."
|
|
down
|
|
sleep 3
|
|
up
|
|
}
|
|
|
|
build() {
|
|
log_step "Building Docker images..."
|
|
|
|
if [ ! -f "$ENV_FILE" ]; then
|
|
log_error "Environment file not found. Run './deploy.sh install' first."
|
|
exit 1
|
|
fi
|
|
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" build --parallel
|
|
log_info "All images built successfully"
|
|
}
|
|
|
|
build_no_cache() {
|
|
log_step "Building Docker images (no cache)..."
|
|
|
|
if [ ! -f "$ENV_FILE" ]; then
|
|
log_error "Environment file not found. Run './deploy.sh install' first."
|
|
exit 1
|
|
fi
|
|
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" build --no-cache --parallel
|
|
log_info "All images built successfully (no cache)"
|
|
}
|
|
|
|
# ===========================================================================
|
|
# Wait Helpers
|
|
# ===========================================================================
|
|
|
|
wait_for_postgres() {
|
|
log_info "Waiting for PostgreSQL..."
|
|
for i in {1..30}; do
|
|
if docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T postgres pg_isready -U it0 &>/dev/null; then
|
|
log_info "PostgreSQL is ready!"
|
|
return
|
|
fi
|
|
sleep 2
|
|
done
|
|
log_warn "PostgreSQL not ready after 60s, continuing anyway..."
|
|
}
|
|
|
|
wait_for_redis() {
|
|
log_info "Waiting for Redis..."
|
|
for i in {1..30}; do
|
|
if docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T redis redis-cli ping &>/dev/null; then
|
|
log_info "Redis is ready!"
|
|
return
|
|
fi
|
|
sleep 2
|
|
done
|
|
log_warn "Redis not ready after 60s, continuing anyway..."
|
|
}
|
|
|
|
# ===========================================================================
|
|
# Status and Monitoring
|
|
# ===========================================================================
|
|
|
|
status() {
|
|
echo ""
|
|
echo "============================================"
|
|
echo "IT0 Backend Services Status"
|
|
echo "============================================"
|
|
echo ""
|
|
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" ps
|
|
|
|
echo ""
|
|
echo "============================================"
|
|
echo "Service Health Check"
|
|
echo "============================================"
|
|
echo ""
|
|
|
|
health
|
|
}
|
|
|
|
health() {
|
|
# Format: "service-name:port:health-endpoint"
|
|
local services=(
|
|
"auth-service:3001:/api/health"
|
|
"agent-service:3002:/api/health"
|
|
"ops-service:3003:/api/health"
|
|
"inventory-service:3004:/api/health"
|
|
"monitor-service:3005:/api/health"
|
|
"comm-service:3006:/api/health"
|
|
"audit-service:3007:/api/health"
|
|
"voice-service:3008:/health"
|
|
"api-gateway:8000:/status"
|
|
)
|
|
|
|
echo "Application Services:"
|
|
for svc in "${services[@]}"; do
|
|
name="${svc%%:*}"
|
|
rest="${svc#*:}"
|
|
port="${rest%%:*}"
|
|
endpoint="${rest#*:}"
|
|
|
|
if curl -s -o /dev/null -w "%{http_code}" "http://localhost:${port}${endpoint}" 2>/dev/null | grep -q "200"; then
|
|
echo -e " ${GREEN}[OK]${NC} $name (port $port)"
|
|
else
|
|
echo -e " ${RED}[FAIL]${NC} $name (port $port)"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo "Infrastructure:"
|
|
|
|
if docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T postgres pg_isready -U it0 &>/dev/null; then
|
|
echo -e " ${GREEN}[OK]${NC} PostgreSQL (port 5432)"
|
|
else
|
|
echo -e " ${RED}[FAIL]${NC} PostgreSQL (port 5432)"
|
|
fi
|
|
|
|
if docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T redis redis-cli ping &>/dev/null; then
|
|
echo -e " ${GREEN}[OK]${NC} Redis (port 6379)"
|
|
else
|
|
echo -e " ${RED}[FAIL]${NC} Redis (port 6379)"
|
|
fi
|
|
}
|
|
|
|
logs() {
|
|
local service="$1"
|
|
|
|
if [ -n "$service" ]; then
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" logs -f "$service"
|
|
else
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" logs -f
|
|
fi
|
|
}
|
|
|
|
# ===========================================================================
|
|
# Database Operations (TypeORM)
|
|
# ===========================================================================
|
|
|
|
migrate() {
|
|
log_step "Running TypeORM migrations..."
|
|
|
|
local services=(
|
|
"auth-service"
|
|
"agent-service"
|
|
"ops-service"
|
|
"inventory-service"
|
|
"monitor-service"
|
|
"comm-service"
|
|
"audit-service"
|
|
)
|
|
|
|
for svc in "${services[@]}"; do
|
|
log_info "Running migrations for $svc..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T "$svc" \
|
|
npx typeorm migration:run -d dist/infrastructure/database/data-source.js 2>/dev/null || \
|
|
log_warn "Migration skipped for $svc (no migrations or service not running)"
|
|
done
|
|
|
|
log_info "Migrations complete"
|
|
}
|
|
|
|
migrate_generate() {
|
|
local service="$1"
|
|
local name="$2"
|
|
|
|
if [ -z "$service" ] || [ -z "$name" ]; then
|
|
log_error "Usage: ./deploy.sh migrate-generate <service-name> <migration-name>"
|
|
exit 1
|
|
fi
|
|
|
|
log_step "Generating migration for $service: $name..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T "$service" \
|
|
npx typeorm migration:generate -d dist/infrastructure/database/data-source.js \
|
|
"src/infrastructure/database/migrations/$name"
|
|
|
|
log_info "Migration generated for $service"
|
|
}
|
|
|
|
migrate_revert() {
|
|
local service="$1"
|
|
|
|
if [ -z "$service" ]; then
|
|
log_error "Usage: ./deploy.sh migrate-revert <service-name>"
|
|
exit 1
|
|
fi
|
|
|
|
log_warn "This will revert the last migration for $service!"
|
|
read -p "Are you sure? (y/N): " confirm
|
|
|
|
if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
|
|
log_info "Migration revert cancelled"
|
|
exit 0
|
|
fi
|
|
|
|
log_step "Reverting last migration for $service..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T "$service" \
|
|
npx typeorm migration:revert -d dist/infrastructure/database/data-source.js
|
|
|
|
log_info "Migration reverted for $service"
|
|
}
|
|
|
|
schema_sync() {
|
|
local service="$1"
|
|
|
|
if [ -z "$service" ]; then
|
|
log_error "Usage: ./deploy.sh schema-sync <service-name>"
|
|
exit 1
|
|
fi
|
|
|
|
log_warn "This will force sync schema for $service (may cause data loss)!"
|
|
read -p "Are you sure? (y/N): " confirm
|
|
|
|
if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
|
|
log_info "Schema sync cancelled"
|
|
exit 0
|
|
fi
|
|
|
|
log_step "Syncing schema for $service..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T "$service" \
|
|
npx typeorm schema:sync -d dist/infrastructure/database/data-source.js
|
|
|
|
log_info "Schema sync complete for $service"
|
|
}
|
|
|
|
# ===========================================================================
|
|
# Cleanup
|
|
# ===========================================================================
|
|
|
|
clean() {
|
|
log_warn "This will remove all containers, volumes, and images!"
|
|
read -p "Are you sure? (y/N): " confirm
|
|
|
|
if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
|
|
log_step "Cleaning up..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" down -v --rmi all
|
|
docker image prune -f
|
|
log_info "Cleanup complete"
|
|
else
|
|
log_info "Cleanup cancelled"
|
|
fi
|
|
}
|
|
|
|
# ===========================================================================
|
|
# Infrastructure Operations
|
|
# ===========================================================================
|
|
|
|
infra_up() {
|
|
log_step "Starting infrastructure services..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d postgres redis
|
|
wait_for_postgres
|
|
wait_for_redis
|
|
log_info "Infrastructure services started"
|
|
}
|
|
|
|
infra_down() {
|
|
log_step "Stopping infrastructure services..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" stop postgres redis
|
|
log_info "Infrastructure services stopped"
|
|
}
|
|
|
|
infra_restart() {
|
|
log_step "Restarting infrastructure services..."
|
|
infra_down
|
|
sleep 3
|
|
infra_up
|
|
}
|
|
|
|
infra_status() {
|
|
echo ""
|
|
echo "============================================"
|
|
echo "Infrastructure Status"
|
|
echo "============================================"
|
|
echo ""
|
|
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" ps postgres redis
|
|
|
|
echo ""
|
|
echo "Health Check:"
|
|
|
|
if docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T postgres pg_isready -U it0 &>/dev/null; then
|
|
echo -e " ${GREEN}[OK]${NC} PostgreSQL (port 5432)"
|
|
else
|
|
echo -e " ${RED}[FAIL]${NC} PostgreSQL (port 5432)"
|
|
fi
|
|
|
|
if docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T redis redis-cli ping &>/dev/null; then
|
|
echo -e " ${GREEN}[OK]${NC} Redis (port 6379)"
|
|
else
|
|
echo -e " ${RED}[FAIL]${NC} Redis (port 6379)"
|
|
fi
|
|
}
|
|
|
|
infra_logs() {
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" logs -f postgres redis
|
|
}
|
|
|
|
infra_clean() {
|
|
log_warn "This will remove infrastructure containers and ALL DATA (postgres, redis)!"
|
|
log_warn "All databases will be DELETED!"
|
|
read -p "Are you sure? (y/N): " confirm
|
|
|
|
if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
|
|
log_step "Stopping infrastructure services..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" stop postgres redis
|
|
|
|
log_step "Removing infrastructure containers..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" rm -f postgres redis
|
|
|
|
log_step "Removing infrastructure volumes..."
|
|
docker volume rm -f docker_postgres_data 2>/dev/null || true
|
|
|
|
log_info "Infrastructure cleanup complete"
|
|
log_info ""
|
|
log_info "To reinstall, run:"
|
|
log_info " ./deploy.sh infra-up"
|
|
log_info " ./deploy.sh migrate"
|
|
else
|
|
log_info "Cleanup cancelled"
|
|
fi
|
|
}
|
|
|
|
infra_reset() {
|
|
log_warn "This will RESET all infrastructure (clean + reinstall)!"
|
|
log_warn "All databases will be DELETED and recreated!"
|
|
read -p "Are you sure? (y/N): " confirm
|
|
|
|
if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
|
|
log_step "Stopping infrastructure services..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" stop postgres redis
|
|
|
|
log_step "Removing infrastructure containers..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" rm -f postgres redis
|
|
|
|
log_step "Removing infrastructure volumes..."
|
|
docker volume rm -f docker_postgres_data 2>/dev/null || true
|
|
|
|
log_step "Starting fresh infrastructure..."
|
|
sleep 3
|
|
infra_up
|
|
|
|
log_info "Infrastructure reset complete!"
|
|
log_info ""
|
|
log_info "Next steps:"
|
|
log_info " 1. Restart application services: ./deploy.sh restart"
|
|
log_info " 2. Run migrations: ./deploy.sh migrate"
|
|
else
|
|
log_info "Reset cancelled"
|
|
fi
|
|
}
|
|
|
|
# ===========================================================================
|
|
# Voice Service Operations (GPU support)
|
|
# ===========================================================================
|
|
|
|
voice_up() {
|
|
log_step "Starting voice service with GPU support..."
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_VOICE_FILE" --env-file "$ENV_FILE" up -d voice-service
|
|
log_info "Voice service started (GPU mode)"
|
|
}
|
|
|
|
voice_down() {
|
|
log_step "Stopping voice service..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" stop voice-service
|
|
log_info "Voice service stopped"
|
|
}
|
|
|
|
voice_logs() {
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" logs -f voice-service
|
|
}
|
|
|
|
voice_rebuild() {
|
|
log_step "Rebuilding voice service..."
|
|
local no_cache="$1"
|
|
if [ "$no_cache" = "--no-cache" ]; then
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_VOICE_FILE" --env-file "$ENV_FILE" build --no-cache voice-service
|
|
else
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_VOICE_FILE" --env-file "$ENV_FILE" build voice-service
|
|
fi
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_VOICE_FILE" --env-file "$ENV_FILE" up -d voice-service
|
|
log_info "Voice service rebuilt and started"
|
|
}
|
|
|
|
# ===========================================================================
|
|
# SSL / Let's Encrypt Operations
|
|
# ===========================================================================
|
|
|
|
ssl_init() {
|
|
log_step "Obtaining Let's Encrypt SSL certificates..."
|
|
log_info "Domains: $API_DOMAIN, $WEB_DOMAIN"
|
|
log_info "Email: $CERT_EMAIL"
|
|
|
|
if [ ! -f "$ENV_FILE" ]; then
|
|
log_error "Environment file not found. Run './deploy.sh install' first."
|
|
exit 1
|
|
fi
|
|
|
|
# Ensure services are running (nginx needs upstream targets)
|
|
log_info "Ensuring backend services are running..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d
|
|
|
|
# Step 1: Start nginx with HTTP-only config for ACME challenge
|
|
log_info "Starting Nginx with HTTP-only config for certificate verification..."
|
|
cp "$SCRIPT_DIR/nginx/nginx-init.conf" "$SCRIPT_DIR/nginx/nginx-active.conf"
|
|
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_SSL_FILE" --env-file "$ENV_FILE" up -d nginx
|
|
|
|
sleep 3
|
|
|
|
# Step 2: Obtain certificate for API domain
|
|
log_step "Obtaining certificate for $API_DOMAIN..."
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_SSL_FILE" --env-file "$ENV_FILE" run --rm certbot \
|
|
certbot certonly --webroot \
|
|
--webroot-path=/var/www/certbot \
|
|
--email "$CERT_EMAIL" \
|
|
--agree-tos \
|
|
--no-eff-email \
|
|
-d "$API_DOMAIN"
|
|
|
|
if [ $? -eq 0 ]; then
|
|
log_info "Certificate obtained for $API_DOMAIN"
|
|
else
|
|
log_error "Failed to obtain certificate for $API_DOMAIN"
|
|
log_error "Make sure DNS for $API_DOMAIN points to this server"
|
|
exit 1
|
|
fi
|
|
|
|
# Step 3: Obtain certificate for Web Admin domain
|
|
log_step "Obtaining certificate for $WEB_DOMAIN..."
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_SSL_FILE" --env-file "$ENV_FILE" run --rm certbot \
|
|
certbot certonly --webroot \
|
|
--webroot-path=/var/www/certbot \
|
|
--email "$CERT_EMAIL" \
|
|
--agree-tos \
|
|
--no-eff-email \
|
|
-d "$WEB_DOMAIN"
|
|
|
|
if [ $? -eq 0 ]; then
|
|
log_info "Certificate obtained for $WEB_DOMAIN"
|
|
else
|
|
log_error "Failed to obtain certificate for $WEB_DOMAIN"
|
|
log_error "Make sure DNS for $WEB_DOMAIN points to this server"
|
|
exit 1
|
|
fi
|
|
|
|
# Step 4: Switch to full SSL nginx config and reload
|
|
log_step "Switching to full SSL configuration..."
|
|
rm -f "$SCRIPT_DIR/nginx/nginx-active.conf"
|
|
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_SSL_FILE" --env-file "$ENV_FILE" up -d nginx
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_SSL_FILE" --env-file "$ENV_FILE" exec -T nginx nginx -s reload 2>/dev/null || \
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_SSL_FILE" --env-file "$ENV_FILE" restart nginx
|
|
|
|
# Start certbot auto-renewal sidecar
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_SSL_FILE" --env-file "$ENV_FILE" up -d certbot
|
|
|
|
log_info ""
|
|
log_info "SSL certificates obtained successfully!"
|
|
log_info " API: https://$API_DOMAIN"
|
|
log_info " Admin: https://$WEB_DOMAIN"
|
|
log_info ""
|
|
log_info "Certificates will auto-renew via the certbot container."
|
|
}
|
|
|
|
ssl_up() {
|
|
log_step "Starting services with SSL (Nginx + Certbot)..."
|
|
|
|
if [ ! -f "$ENV_FILE" ]; then
|
|
log_error "Environment file not found. Run './deploy.sh install' first."
|
|
exit 1
|
|
fi
|
|
|
|
# Start all services including nginx + certbot
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_SSL_FILE" --env-file "$ENV_FILE" up -d
|
|
|
|
log_info "All services started with SSL!"
|
|
log_info " API: https://$API_DOMAIN"
|
|
log_info " Admin: https://$WEB_DOMAIN"
|
|
}
|
|
|
|
ssl_down() {
|
|
log_step "Stopping all services including SSL proxy..."
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_SSL_FILE" --env-file "$ENV_FILE" down
|
|
log_info "All services stopped"
|
|
}
|
|
|
|
ssl_renew() {
|
|
log_step "Manually renewing SSL certificates..."
|
|
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_SSL_FILE" --env-file "$ENV_FILE" run --rm certbot \
|
|
certbot renew --webroot -w /var/www/certbot
|
|
|
|
# Reload nginx to pick up new certs
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_SSL_FILE" --env-file "$ENV_FILE" exec -T nginx nginx -s reload
|
|
|
|
log_info "Certificate renewal complete"
|
|
}
|
|
|
|
ssl_status() {
|
|
echo ""
|
|
echo "============================================"
|
|
echo "SSL Certificate Status"
|
|
echo "============================================"
|
|
echo ""
|
|
|
|
for domain in "$API_DOMAIN" "$WEB_DOMAIN"; do
|
|
echo "Domain: $domain"
|
|
docker compose -f "$COMPOSE_FILE" -f "$COMPOSE_SSL_FILE" --env-file "$ENV_FILE" run --rm certbot \
|
|
certbot certificates -d "$domain" 2>/dev/null || echo " No certificate found"
|
|
echo ""
|
|
done
|
|
|
|
echo "Nginx Status:"
|
|
if docker ps --filter "name=it0-nginx" --format "{{.Status}}" | grep -q "Up"; then
|
|
echo -e " ${GREEN}[OK]${NC} Nginx is running"
|
|
else
|
|
echo -e " ${RED}[FAIL]${NC} Nginx is not running"
|
|
fi
|
|
|
|
echo "Certbot Status:"
|
|
if docker ps --filter "name=it0-certbot" --format "{{.Status}}" | grep -q "Up"; then
|
|
echo -e " ${GREEN}[OK]${NC} Certbot auto-renewal is active"
|
|
else
|
|
echo -e " ${YELLOW}[WARN]${NC} Certbot auto-renewal is not running"
|
|
fi
|
|
}
|
|
|
|
# ===========================================================================
|
|
# Single Service Operations
|
|
# ===========================================================================
|
|
|
|
start_service() {
|
|
local service="$1"
|
|
if [ -z "$service" ]; then
|
|
log_error "Please specify a service name"
|
|
exit 1
|
|
fi
|
|
|
|
log_info "Starting $service..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d "$service"
|
|
}
|
|
|
|
stop_service() {
|
|
local service="$1"
|
|
if [ -z "$service" ]; then
|
|
log_error "Please specify a service name"
|
|
exit 1
|
|
fi
|
|
|
|
log_info "Stopping $service..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" stop "$service"
|
|
}
|
|
|
|
rebuild_service() {
|
|
local service="$1"
|
|
local no_cache="$2"
|
|
if [ -z "$service" ]; then
|
|
log_error "Please specify a service name"
|
|
exit 1
|
|
fi
|
|
|
|
log_info "Rebuilding $service..."
|
|
if [ "$no_cache" = "--no-cache" ]; then
|
|
log_info "Building without cache..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" build --no-cache "$service"
|
|
else
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" build "$service"
|
|
fi
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d "$service"
|
|
}
|
|
|
|
service_logs() {
|
|
local service="$1"
|
|
if [ -z "$service" ]; then
|
|
log_error "Please specify a service name"
|
|
exit 1
|
|
fi
|
|
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" logs -f "$service"
|
|
}
|
|
|
|
# ===========================================================================
|
|
# Main
|
|
# ===========================================================================
|
|
|
|
case "${1:-}" in
|
|
install)
|
|
install
|
|
;;
|
|
up|start)
|
|
up
|
|
;;
|
|
down|stop)
|
|
down
|
|
;;
|
|
restart)
|
|
restart
|
|
;;
|
|
build)
|
|
build
|
|
;;
|
|
build-no-cache)
|
|
build_no_cache
|
|
;;
|
|
status|ps)
|
|
status
|
|
;;
|
|
health)
|
|
health
|
|
;;
|
|
logs)
|
|
logs "$2"
|
|
;;
|
|
migrate)
|
|
migrate
|
|
;;
|
|
migrate-generate)
|
|
migrate_generate "$2" "$3"
|
|
;;
|
|
migrate-revert)
|
|
migrate_revert "$2"
|
|
;;
|
|
schema-sync)
|
|
schema_sync "$2"
|
|
;;
|
|
clean)
|
|
clean
|
|
;;
|
|
start-svc)
|
|
start_service "$2"
|
|
;;
|
|
stop-svc)
|
|
stop_service "$2"
|
|
;;
|
|
rebuild-svc)
|
|
rebuild_service "$2" "$3"
|
|
;;
|
|
svc-logs)
|
|
service_logs "$2"
|
|
;;
|
|
infra-up)
|
|
infra_up
|
|
;;
|
|
infra-down)
|
|
infra_down
|
|
;;
|
|
infra-restart)
|
|
infra_restart
|
|
;;
|
|
infra-status)
|
|
infra_status
|
|
;;
|
|
infra-logs)
|
|
infra_logs
|
|
;;
|
|
infra-clean)
|
|
infra_clean
|
|
;;
|
|
infra-reset)
|
|
infra_reset
|
|
;;
|
|
voice-up)
|
|
voice_up
|
|
;;
|
|
voice-down)
|
|
voice_down
|
|
;;
|
|
voice-logs)
|
|
voice_logs
|
|
;;
|
|
voice-rebuild)
|
|
voice_rebuild "$2"
|
|
;;
|
|
ssl-init)
|
|
ssl_init
|
|
;;
|
|
ssl-up)
|
|
ssl_up
|
|
;;
|
|
ssl-down)
|
|
ssl_down
|
|
;;
|
|
ssl-renew)
|
|
ssl_renew
|
|
;;
|
|
ssl-status)
|
|
ssl_status
|
|
;;
|
|
*)
|
|
echo "IT0 Backend Services Deployment Script"
|
|
echo ""
|
|
echo "Usage: $0 <command> [options]"
|
|
echo ""
|
|
echo "Commands:"
|
|
echo " install - First time setup (generate secrets, create configs)"
|
|
echo " up/start - Start all services"
|
|
echo " down/stop - Stop all services"
|
|
echo " restart - Restart all services"
|
|
echo " build - Build all Docker images"
|
|
echo " build-no-cache - Build all images without cache"
|
|
echo " status/ps - Show service status"
|
|
echo " health - Check health of all services"
|
|
echo " logs [svc] - View logs (optionally for specific service)"
|
|
echo " clean - Remove all containers, volumes, and images"
|
|
echo ""
|
|
echo "Database Commands (TypeORM):"
|
|
echo " migrate - Run migrations for all services"
|
|
echo " migrate-generate <svc> <name> - Generate a new migration"
|
|
echo " migrate-revert <svc> - Revert last migration"
|
|
echo " schema-sync <svc> - Force sync schema (DEV ONLY)"
|
|
echo ""
|
|
echo "Single Service Commands:"
|
|
echo " start-svc <name> - Start a specific service"
|
|
echo " stop-svc <name> - Stop a specific service"
|
|
echo " rebuild-svc <name> [--no-cache] - Rebuild and restart a specific service"
|
|
echo " svc-logs <name> - View logs for a specific service"
|
|
echo ""
|
|
echo "Infrastructure Commands:"
|
|
echo " infra-up - Start infrastructure (postgres, redis)"
|
|
echo " infra-down - Stop infrastructure services"
|
|
echo " infra-restart - Restart infrastructure services"
|
|
echo " infra-status - Show infrastructure status and health"
|
|
echo " infra-logs - View infrastructure logs"
|
|
echo " infra-clean - Remove infrastructure containers and volumes (DELETES DATA)"
|
|
echo " infra-reset - Clean and reinstall infrastructure (DELETES DATA)"
|
|
echo ""
|
|
echo "Voice Service Commands (GPU):"
|
|
echo " voice-up - Start voice service with GPU support"
|
|
echo " voice-down - Stop voice service"
|
|
echo " voice-logs - View voice service logs"
|
|
echo " voice-rebuild [--no-cache] - Rebuild voice service"
|
|
echo ""
|
|
echo "SSL / Let's Encrypt Commands:"
|
|
echo " ssl-init - Obtain SSL certificates for both domains"
|
|
echo " ssl-up - Start all services with Nginx SSL proxy"
|
|
echo " ssl-down - Stop all services including SSL proxy"
|
|
echo " ssl-renew - Manually renew certificates"
|
|
echo " ssl-status - Check certificate and proxy status"
|
|
echo ""
|
|
echo "Domains:"
|
|
echo " API: https://it0api.szaiai.com (configurable via API_DOMAIN)"
|
|
echo " Admin: https://it0.szaiai.com (configurable via WEB_DOMAIN)"
|
|
echo ""
|
|
echo "Services:"
|
|
echo " auth-service, agent-service, ops-service, inventory-service,"
|
|
echo " monitor-service, comm-service, audit-service, voice-service,"
|
|
echo " api-gateway, web-admin"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 install # First time setup"
|
|
echo " $0 build # Build images"
|
|
echo " $0 up # Start all services"
|
|
echo " $0 logs agent-service # View agent-service logs"
|
|
echo " $0 rebuild-svc auth-service # Rebuild specific service"
|
|
echo " $0 voice-up # Start voice with GPU"
|
|
echo " $0 migrate # Run all migrations"
|
|
echo " $0 migrate-generate auth-service AddUserTable # Generate migration"
|
|
echo " $0 ssl-init # First time SSL setup"
|
|
echo " $0 ssl-up # Start with SSL"
|
|
echo ""
|
|
exit 1
|
|
;;
|
|
esac
|