it0/deploy/docker/deploy.sh

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