#!/bin/bash set -eu set -o pipefail 2>/dev/null || true BOLD=$(tput bold) GREEN=$(tput setaf 2) YELLOW=$(tput setaf 3) RED=$(tput setaf 1) CYAN=$(tput setaf 6) RESET=$(tput sgr0) command_exists() { command -v "$1" >/dev/null 2>&1 } generate_secret() { openssl rand -hex 16 } echo_bold() { echo "${BOLD}$1${RESET}"; } echo_green() { echo "${GREEN}$1${RESET}"; } echo_yellow() { echo "${YELLOW}$1${RESET}"; } echo_cyan() { echo "${CYAN}$1${RESET}"; } echo_bold "${CYAN}⛵ ZenSailor Production Installer${RESET}" echo "This script will set up a production-ready ZenSailor instance in /opt/zensailor." echo echo_yellow "This script requires sudo privileges to install software and manage files in /opt." sudo -v echo echo_yellow "STEP 1: Checking for dependencies..." if ! command_exists docker; then echo_yellow "Docker is not installed. Installing Docker automatically..." if curl -fsSL https://get.docker.com -o get-docker.sh; then sudo sh get-docker.sh rm -f get-docker.sh echo_green "✔ Docker installed successfully." else echo_bold "${RED}Error: Failed to download Docker installer.${RESET}" exit 1 fi else echo_green "✔ Docker is already installed." fi if ! docker compose version >/dev/null 2>&1; then echo_yellow "Docker Compose V2 not found. Attempting to install plugin..." sudo apt-get update 2>/dev/null || true sudo apt-get install -y docker-compose-plugin 2>/dev/null || \ sudo yum install -y docker-compose-plugin 2>/dev/null || \ echo_bold "${YELLOW}Warning: Could not auto-install Docker Compose. Please check manually.${RESET}" else echo_green "✔ Docker Compose detected." fi for cmd in curl tar openssl getent; do if ! command_exists "$cmd"; then echo_yellow "Installing missing dependency: $cmd..." if command_exists apt-get; then sudo apt-get update -qq && sudo apt-get install -y -qq "$cmd" elif command_exists yum; then sudo yum install -y -q "$cmd" elif command_exists apk; then sudo apk add -q "$cmd" else echo "${RED}Error: Dependency '$cmd' is missing and could not be auto-installed.${RESET}" exit 1 fi fi done echo_green "✔ All dependencies are satisfied." echo echo_yellow "STEP 2: Please provide some configuration details:" DOMAIN_NAME="" ACME_EMAIL="" while [[ -z "$DOMAIN_NAME" || ! "$DOMAIN_NAME" =~ ^[a-zA-Z0-9.-]+$ ]]; do read -r -p "${BOLD}Enter the public domain for ZenSailor (e.g., zensailor.mycompany.com): ${RESET}" DOMAIN_NAME /dev/null 2>&1; then echo_bold "${RED}WARNING: An existing database volume 'zensailor-db-data-prod' was found.${RESET}" echo "If you are reinstalling, the new random password will NOT match the old database password." echo "This will cause 'Authentication failed' errors." echo read -r -p "Do you want to DELETE the existing database volume to start fresh? (y/N) " DELETE_VOL "$INSTALL_DIR/.env" << EOF IMAGE_TAG=${ZENSAILOR_TAG} POSTGRES_DB=${POSTGRES_DB} POSTGRES_USER=${POSTGRES_USER} POSTGRES_PASSWORD=${POSTGRES_PASSWORD} DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@zensailor-postgres-prod:5432/${POSTGRES_DB}?schema=public APP_URL=${APP_URL} NEXT_PUBLIC_APP_URL=${APP_URL} JWT_SECRET=${JWT_SECRET} JWT_EXPIRES_IN_DAYS=7 ENCRYPTION_KEY=${ENCRYPTION_KEY} REGISTRY_URL=${REGISTRY_URL} UPDATES_REGISTRY_URL=${UPDATES_REGISTRY_URL} DIRECT_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@zensailor-postgres-prod:5432/${POSTGRES_DB}?schema=public PREMIUM_EMAILS= NATS_URL=nats://zensailor-nats-prod:4222 ACME_EMAIL=${ACME_EMAIL} ACTIVE_COLOR=blue COLOR=blue DOCKER_GID=${DOCKER_GID} EOF echo_green "✔ .env file created successfully." echo echo_yellow "STEP 5: Setting up initial Traefik configuration..." cd "$INSTALL_DIR" cat > traefik-config/dynamic/zensailor-router.yml << EOF http: routers: zensailor-dashboard: rule: "Host(\`${DOMAIN_NAME}\`)" entryPoints: - websecure service: "zensailor-control-plane-blue@docker" tls: certResolver: "letsencrypt" zensailor-dashboard-http: rule: "Host(\`${DOMAIN_NAME}\`)" entryPoints: - web middlewares: - https-redirect service: "zensailor-control-plane-blue@docker" middlewares: https-redirect: redirectScheme: scheme: https permanent: true security-headers: headers: frameDeny: true contentTypeNosniff: true browserXssFilter: true forceSTSHeader: true stsIncludeSubdomains: true stsPreload: true stsSeconds: 31536000 EOF echo_green "✔ Initial router created, pointing to 'blue' deployment." echo echo_yellow "STEP 6: Starting the ZenSailor production stack..." echo_yellow "Pulling required Docker images... (This may take a moment)" docker compose -f docker-compose.prod.yml pull echo_yellow "Starting ZenSailor services..." docker compose -f docker-compose.prod.yml up -d echo echo_green "⛵ ZenSailor installation is complete! ⛵" echo echo "Your ZenSailor dashboard should be available shortly at:" echo " ${BOLD}${CYAN}${APP_URL}${RESET}" echo echo "It may take a minute for the SSL certificate to be issued." echo "The first user to sign up will become the platform administrator." echo echo "To manage your installation, navigate to the '${BOLD}${INSTALL_DIR}${RESET}' directory." echo " - View logs: ${BOLD}cd ${INSTALL_DIR} && docker compose -f docker-compose.prod.yml logs -f${RESET}" echo " - Stop: ${BOLD}cd ${INSTALL_DIR} && docker compose -f docker-compose.prod.yml down${RESET}"