#!/usr/bin/env bash set -euo pipefail REPO_DIR="/opt/NextWks" TARGET_DIR="/opt/nextworkspace" BACKUP_DIR="/opt/backup" SERVICE_NAME="nextworkspace" BINARY_NAME="nextworkspace" HEALTH_CHECK_RETRIES=10 HEALTH_CHECK_INTERVAL=2 # --- Load .env from backup vault (written by install.sh) --- if [ -f "$BACKUP_DIR/.env" ]; then set -a source "$BACKUP_DIR/.env" set +a DOMAIN="${DOMAIN:-nextwks.eu}" elif [ -f "$TARGET_DIR/.env" ]; then set -a source "$TARGET_DIR/.env" set +a DOMAIN="${DOMAIN:-nextwks.eu}" else DOMAIN="${DOMAIN:-nextwks.eu}" fi # --- Mode detection --- GREENFIELD=false if [ "${1:-}" = "--destroy" ]; then GREENFIELD=true echo "[MODE] Greenfield deploy (--destroy)" elif [ ! -d "$TARGET_DIR" ]; then GREENFIELD=true echo "[MODE] Greenfield deploy (target missing)" else echo "[MODE] Smart update (target exists)" fi # --- Common: pull + build --- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" cd "$REPO_DIR" echo "[1/6] Pulling latest code..." git pull echo "[2/6] Building binary and helper tool..." export PATH=$PATH:/usr/local/go/bin go build -o "$BINARY_NAME" . # Build helper tool from script directory (has correct go.mod with all deps) TOOL_BIN="/tmp/nextwks-tool" TOOL_SRC="$SCRIPT_DIR/tools/nextwks-tool" if [ -d "$TOOL_SRC" ]; then cd "$TOOL_SRC" go build -o "$TOOL_BIN" . 2>&1 && echo "[OK] Helper tool built" || echo "[WARN] Helper tool build failed" cd "$REPO_DIR" fi # Install lego if not present (for LE certificate management) if ! command -v lego &>/dev/null && [ -f "$TOOL_BIN" ]; then echo "[*] Installing lego ACME client..." "$TOOL_BIN" install-lego 2>&1 || echo "[WARN] Lego install failed" fi # --- Greenfield path --- if [ "$GREENFIELD" = true ]; then # Step 3: Full teardown — stop, disable, remove all services echo "[3/6] Full service teardown..." systemctl stop $SERVICE_NAME 2>/dev/null || true systemctl disable $SERVICE_NAME 2>/dev/null || true rm -f /etc/systemd/system/$SERVICE_NAME.service systemctl daemon-reload podman rm -f zoraxy 2>/dev/null || true # Wait for port 80 to be released for i in $(seq 1 10); do if ! ss -tlnp | grep -q ':80 '; then break fi echo " Waiting for port 80 to be released... ($i/10)" sleep 1 done # Wipe production directory if [ -d "$TARGET_DIR" ]; then chattr -R -i "$TARGET_DIR" 2>/dev/null || true rm -rf "$TARGET_DIR" fi # Step 4: Run helper tool — port 80 is free, can obtain LE certs via HTTP-01 echo "[4/6] Checking certificates..." mkdir -p "$BACKUP_DIR/certificates" if [ -f "$TOOL_BIN" ]; then "$TOOL_BIN" cert \ --domains "app.${DOMAIN},dns.${DOMAIN},www.${DOMAIN}" \ --email "${TLS_EMAIL:-admin@${DOMAIN}}" \ --backup-dir "$BACKUP_DIR/certificates" 2>&1 || true fi # Step 5: Build production directory structure echo "[5/6] Building production directory structure..." mkdir -p "$TARGET_DIR/config/nextworkspace" mkdir -p "$TARGET_DIR/config/zoraxy/conf/proxy" mkdir -p "$TARGET_DIR/config/zoraxy/conf/certs" mkdir -p "$TARGET_DIR/config/zoraxy/www/html" mkdir -p "$TARGET_DIR/data/zoraxy" mkdir -p "$TARGET_DIR/compose" mkdir -p "$TARGET_DIR/logs" # Copy .env from backup vault if [ -f "$BACKUP_DIR/.env" ]; then cp "$BACKUP_DIR/.env" "$TARGET_DIR/.env" chmod 600 "$TARGET_DIR/.env" echo "[INFO] .env deployed from backup" fi # Copy certificates from backup vault to Zoraxy cert dir for DOMAIN_SUB in app dns www; do FQDN="${DOMAIN_SUB}.${DOMAIN}" CERT_SRC="$BACKUP_DIR/certificates/${FQDN}/fullchain.pem" KEY_SRC="$BACKUP_DIR/certificates/${FQDN}/privkey.pem" if [ -f "$CERT_SRC" ] && [ -f "$KEY_SRC" ]; then cp "$CERT_SRC" "$TARGET_DIR/config/zoraxy/conf/certs/${FQDN}.crt" cp "$KEY_SRC" "$TARGET_DIR/config/zoraxy/conf/certs/${FQDN}.key" echo "[INFO] Cert deployed: ${FQDN}" fi done # Copy binary echo "[6/6] Deploying..." cp "$BINARY_NAME" "$TARGET_DIR/$BINARY_NAME" if [ -f "$REPO_DIR/VERSION" ]; then cp "$REPO_DIR/VERSION" "$TARGET_DIR/VERSION" echo "[INFO] Version: $(cat $TARGET_DIR/VERSION)" fi # Write configs before starting Zoraxy (BoltDB must not be locked) echo "[*] Writing configuration files..." # Write ACME config mkdir -p "$TARGET_DIR/config/zoraxy/conf" cat > "$TARGET_DIR/config/zoraxy/conf/acme_conf.json" <&1 || true "$TOOL_BIN" db --db "$TARGET_DIR/data/zoraxy/sys.db" \ --set "zorxauth:options:{\"enable_auth_gateway\":true,\"sso_redirect_url\":\"https://app.${DOMAIN}/\"}" 2>&1 || true echo " [OK] Admin + ZorxAuth written to BoltDB" fi # Deploy Zoraxy cp compose/zoraxy.yaml "$TARGET_DIR/compose/zoraxy.yaml" podman rm -f zoraxy 2>/dev/null || true podman-compose -f "$TARGET_DIR/compose/zoraxy.yaml" up -d 2>&1 || echo "[WARN] Zoraxy deploy had issues" # Generate Zoraxy proxy configs echo "[*] Generating Zoraxy proxy configs..." cat > "$TARGET_DIR/config/zoraxy/conf/proxy/app.$DOMAIN.config" < "$TARGET_DIR/config/zoraxy/conf/proxy/dns.$DOMAIN.config" < "$TARGET_DIR/config/zoraxy/conf/proxy/www.$DOMAIN.config" < "$TARGET_DIR/config/zoraxy/conf/proxy/auth.$DOMAIN.config" </dev/null || true # Copy landing page cp -r config/www/* "$TARGET_DIR/config/zoraxy/www/html/" # Copy launcher config cp -r config/nextworkspace/* "$TARGET_DIR/config/nextworkspace/" # Generate apps.yaml echo "[*] Generating apps.yaml..." cat > "$TARGET_DIR/config/nextworkspace/apps.yaml" < /etc/systemd/system/$SERVICE_NAME.service </dev/null || true echo "[4/6] Swapping binary..." cp "$BINARY_NAME" "$TARGET_DIR/$BINARY_NAME" echo "[5/6] Refreshing configs..." if [ -f config/nextworkspace/apps.yaml ]; then cp config/nextworkspace/apps.yaml "$TARGET_DIR/config/nextworkspace/apps.yaml" fi if [ -d config/zoraxy/conf/proxy ]; then cp config/zoraxy/conf/proxy/* "$TARGET_DIR/config/zoraxy/conf/proxy/" 2>/dev/null || true fi echo "[6/6] Restarting Zoraxy and launcher..." podman-compose -f "$TARGET_DIR/compose/zoraxy.yaml" restart 2>&1 || true systemctl restart $SERVICE_NAME fi # --- Health check --- echo "[*] Running health check..." for i in $(seq 1 $HEALTH_CHECK_RETRIES); do if curl -sf http://127.0.0.1:9000/health > /dev/null 2>&1; then echo "[OK] NextWorkspace launcher is healthy on http://127.0.0.1:9000/" exit 0 fi echo " Attempt $i/$HEALTH_CHECK_RETRIES — not ready yet..." sleep $HEALTH_CHECK_INTERVAL done echo "[FAIL] Health check failed — launcher did not respond on port 9000" exit 1