#!/bin/bash set -e echo "=========================================" echo " NexusGuard SD-WAN: SYSTEM UPDATE " echo "=========================================" STATE_FILE=".update-state" FORCE_REBUILD=false BACKUP=false NO_MIGRATE=false # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in --force) FORCE_REBUILD=true shift ;; --backup) BACKUP=true shift ;; --no-migrate) NO_MIGRATE=true shift ;; *) echo "[!] Unknown option: $1" exit 1 ;; esac done # 1. Pastikan file env ada if [ ! -f .env ]; then echo "[-] ERROR: .env file not found. Please run ./setup.sh first to initialize configuration." exit 1 fi # 2. Update Code dari Git (Main & Submodules) echo "[+] Pulling latest source code from repository..." GIT_PULL_FAILED=false git pull origin main || { GIT_PULL_FAILED=true; echo "[!] Git pull for main repo skipped or failed, continuing..."; } echo "[+] Syncing and updating submodules..." SUBMODULE_FAILED=false # Only update server-core and dashboard-ui (Docker components) # device-agent and device-agent-embedded are built separately git submodule update --init --remote apps/server-core apps/dashboard-ui || { SUBMODULE_FAILED=true; echo "[!] Git submodule update skipped or failed."; } # 2b. Build VitePress docs (non-blocking) echo "[+] Building VitePress documentation..." DOCS_BUILD_FAILED=false if [ -d "apps/docs" ]; then (cd apps/docs && npm install && npm run docs:build) || { DOCS_BUILD_FAILED=true; echo "[!] Docs build failed. Documentation may be stale. Continuing deployment..."; } else echo "[!] apps/docs directory not found. Skipping docs build." fi # 3. Hitung state hash (git + .env) untuk deteksi perubahan CURRENT_HASH=$(echo "$(git rev-parse HEAD 2>/dev/null)$(git submodule status 2>/dev/null)$(sha256sum .env 2>/dev/null)" | sha256sum | cut -d" " -f1) # 4. Tentukan apakah perlu rebuild NEEDS_REBUILD=false if [ "$FORCE_REBUILD" = true ]; then echo "[+] --force flag detected. Will rebuild." NEEDS_REBUILD=true elif [ "$GIT_PULL_FAILED" = true ] || [ "$SUBMODULE_FAILED" = true ]; then echo "[!] Git operation failed. Rebuilding as safe fallback." NEEDS_REBUILD=true elif [ ! -f "$STATE_FILE" ]; then echo "[+] First run (no state file found). Full cycle required." NEEDS_REBUILD=true else PREVIOUS_HASH=$(cat "$STATE_FILE" 2>/dev/null || echo "") if [ "$CURRENT_HASH" != "$PREVIOUS_HASH" ]; then echo "[+] State hash changed. Rebuilding." NEEDS_REBUILD=true else echo "[-] No changes detected. Skipping build and restart." fi fi # 5. Rebuild cycle — hanya jika ada perubahan if [ "$NEEDS_REBUILD" = true ]; then # Source .env untuk build process echo "[+] Sourcing environment variables from root .env..." set -a && . .env && set +a # Volume safety check echo "[+] Checking PostgreSQL volume safety..." if ! docker volume inspect pgdata >/dev/null 2>&1; then echo "[!] WARNING: PostgreSQL volume 'pgdata' not found. It will be created on container startup." echo "[!] If you expect existing data, check your volume configuration." else echo "[+] PostgreSQL volume 'pgdata' found." fi # Backup option if [ "$BACKUP" = true ]; then echo "[+] Creating backup of PostgreSQL data..." BACKUP_DIR="./backups" mkdir -p "$BACKUP_DIR" TIMESTAMP=$(date +"%Y%m%d_%H%M%S") BACKUP_FILE="$BACKUP_DIR/pgdata_backup_$TIMESTAMP.tar" # Backup the volume using a temporary container if docker run --rm -v pgdata:/data -v "$(pwd)/$BACKUP_DIR":/backup ubuntu tar czf "/backup/pgdata_backup_$TIMESTAMP.tar.gz" -C /data . 2>/dev/null; then echo "[+] Backup created successfully: $BACKUP_FILE.gz" else echo "[!] Backup failed. Continuing without backup." fi fi # Password sync if DB_PASSWORD changed echo "[+] Checking for PostgreSQL password synchronization..." # Get current DB_PASSWORD from .env CURRENT_DB_PASSWORD="${DB_PASSWORD:-nexusguard}" # Try to connect to existing postgres container and update password if needed POSTGRES_CONTAINER=$(docker compose ps -q postgres 2>/dev/null) if [ -n "$POSTGRES_CONTAINER" ]; then echo "[+] Found running PostgreSQL container. Checking password..." # Test if current password works if docker exec "$POSTGRES_CONTAINER" pg_isready -U nexusguard -d nexusguard 2>/dev/null; then echo "[+] Current password works. No password update needed." else echo "[!] Current password failed. Attempting to update PostgreSQL password to match .env..." # Try to alter user password (requires superuser or same user) if docker exec -u postgres "$POSTGRES_CONTAINER" psql -c "ALTER USER nexusguard WITH PASSWORD '$CURRENT_DB_PASSWORD';" 2>/dev/null; then echo "[+] PostgreSQL password updated successfully." else echo "[!] Failed to update password. You may need to manually update it or check permissions." echo "[!] Continuing update - authentication may fail if password mismatch." fi fi else echo "[i] No running PostgreSQL container found. Password will be set on container initialization (if data directory is empty)." fi echo "[+] Stopping existing containers..." docker compose down echo "[+] Rebuilding Docker images..." docker compose build --build-arg VITE_API_BASE_URL=$VITE_API_BASE_URL echo "[+] Restarting containers..." docker compose up -d if [ "$NO_MIGRATE" = false ]; then echo "[+] Running automated database migrations..." docker exec nexus-guard-suite-server-core-1 ./server-core -migrate-prod || echo "[!] Migration skipped or failed. It might not be needed." else echo "[+] Skipping database migration (--no-migrate flag used)." fi echo "[+] Backfilling interface addresses..." docker exec nexus-guard-suite-server-core-1 ./server-core -backfill-interface 2>/dev/null || echo "[!] Backfill skipped or not needed." echo "[+] Cleaning up unused dangling images..." docker image prune -f # Simpan state secara atomik (tmp + mv untuk cegah korupsi file) echo "$CURRENT_HASH" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE" echo "=========================================" echo " Update & Deployment complete! " echo " Dashboard : http://localhost:${WEB_PORT:-80}" echo " API Port : ${API_PORT:-8080}" if [ "$BACKUP" = true ]; then echo " Backup : Stored in ./backups/ directory" fi echo "=========================================" else echo "=========================================" echo " No update needed — system is current. " echo "=========================================" fi