From 6c4a3362e020883ad8c68f752ace8c9f16b7e2a1 Mon Sep 17 00:00:00 2001 From: EZ-Homelab Assistant Date: Sat, 31 Jan 2026 05:41:30 -0500 Subject: [PATCH] feat: Major UI improvements to ez-homelab.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add professional ASCII box styling to main menu - Implement interactive variable configuration with validation - Add icons to all prompts (🌐 🌍 🦆 🔑 👤 🔒 📧 🏠) - Create vanishing prompts that replace with status confirmations - Add comprehensive menu system with proceed/review/quit options - Show current configuration values before deployment choices - Implement proper error handling for invalid inputs - Add immediate quit functionality with 'q' during any prompt - Improve spacing and visual hierarchy throughout interface - Fix deployment flow to prevent accidental starts on invalid input --- aliases.sh | 4 + config-templates/homepage/docker.yaml | 2 +- config-templates/homepage/services.yaml | 118 ++--- markup.yml | 60 +++ scripts/ez-homelab.sh | 611 +++++++++++++++--------- 5 files changed, 499 insertions(+), 296 deletions(-) create mode 100755 aliases.sh create mode 100644 markup.yml diff --git a/aliases.sh b/aliases.sh new file mode 100755 index 0000000..86d173f --- /dev/null +++ b/aliases.sh @@ -0,0 +1,4 @@ +alias ll='ls -alF' +alias dkrtable='docker ps --format "table {{.Names}}\t{{.Status}}\t{{.RunningFor}}"' +alias dkrrecreate='docker compose up -d --force-recreate' +alias fixpermissions='sudo chown -R 1000:1000 /opt ~' diff --git a/config-templates/homepage/docker.yaml b/config-templates/homepage/docker.yaml index 7080ea2..8666f25 100644 --- a/config-templates/homepage/docker.yaml +++ b/config-templates/homepage/docker.yaml @@ -13,6 +13,6 @@ # host: 192.168.4.5 # port: 2375 -#${SERVER_HOSTNAME}: +#jasper: # host: 192.168.4.11 # port: 2375 diff --git a/config-templates/homepage/services.yaml b/config-templates/homepage/services.yaml index 562f3e0..18c2ef1 100644 --- a/config-templates/homepage/services.yaml +++ b/config-templates/homepage/services.yaml @@ -4,288 +4,288 @@ - Dashboards: - Homepage: icon: homepage.png - href: https://homepage.${DOMAIN} + href: https://homepage.kelinreij.duckdns.org description: Hosted on Raspberry Pi - Homepage - ${REMOTE_SERVER_HOSTNAME}: icon: homepage.png - href: https://homepage.${REMOTE_SERVER_HOSTNAME}.${DOMAIN} + href: https://homepage.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org description: ${REMOTE_SERVER_HOSTNAME} - Application Dashboard - Homarr: icon: homarr.png - href: https://homarr.${DOMAIN} + href: https://homarr.kelinreij.duckdns.org description: Alternative Dashboard - Homarr - ${REMOTE_SERVER_HOSTNAME}: icon: homarr.png - href: https://homarr.${REMOTE_SERVER_HOSTNAME}.${DOMAIN} + href: https://homarr.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org description: ${REMOTE_SERVER_HOSTNAME} - Alternative Dashboard - - Dockge - ${SERVER_HOSTNAME}: + - Dockge - jasper: icon: dockge.png - href: https://${SERVER_HOSTNAME}.${DOMAIN} + href: https://jasper.kelinreij.duckdns.org description: Main Server - Dockge - ${REMOTE_SERVER_HOSTNAME}: icon: dockge.png - href: https://${REMOTE_SERVER_HOSTNAME}.${DOMAIN} + href: https://${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org description: Raspberry Pi Authentication Server - Core: - Traefik: icon: traefik.png - href: https://traefik.${DOMAIN} + href: https://traefik.kelinreij.duckdns.org description: Reverse Proxy & SSL - Authelia: icon: authelia.png - href: https://auth.${DOMAIN} + href: https://auth.kelinreij.duckdns.org description: Authentication SSO Portal - Pi-hole: icon: pi-hole.png - href: https://pihole.${DOMAIN} + href: https://pihole.kelinreij.duckdns.org description: Network-wide Ad Blocking - Monitoring Stack: - Dozzle: icon: dozzle.png - href: https://dozzle.${SERVER_HOSTNAME}.${DOMAIN} - description: ${SERVER_HOSTNAME} - Real-time Log Viewer + href: https://dozzle.jasper.kelinreij.duckdns.org + description: jasper - Real-time Log Viewer - Dozzle: icon: dozzle.png - href: https://dozzle.${REMOTE_SERVER_HOSTNAME}.${DOMAIN} + href: https://dozzle.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org description: ${REMOTE_SERVER_HOSTNAME} - Real-time Log Viewer - - Glances - ${SERVER_HOSTNAME}: + - Glances - jasper: icon: glances.png - href: https://glances.${SERVER_HOSTNAME}.${DOMAIN} - description: ${SERVER_HOSTNAME} - System Monitoring + href: https://glances.jasper.kelinreij.duckdns.org + description: jasper - System Monitoring - Glances - ${REMOTE_SERVER_HOSTNAME}: icon: glances.png - href: https://glances.${REMOTE_SERVER_HOSTNAME}.${DOMAIN} + href: https://glances.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org description: ${REMOTE_SERVER_HOSTNAME} - System Monitoring - Uptime Kuma: icon: uptime-kuma.png - href: https://uptime-kuma.${DOMAIN} + href: https://uptime-kuma.kelinreij.duckdns.org description: Uptime Monitoring - Grafana - ${REMOTE_SERVER_HOSTNAME}: icon: grafana.png - href: https://grafana.${REMOTE_SERVER_HOSTNAME}.${DOMAIN} + href: https://grafana.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org description: ${REMOTE_SERVER_HOSTNAME} - Metrics Dashboard - Prometheus - ${REMOTE_SERVER_HOSTNAME}: icon: prometheus.png - href: https://prometheus.${REMOTE_SERVER_HOSTNAME}.${DOMAIN} + href: https://prometheus.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org description: ${REMOTE_SERVER_HOSTNAME} - Metrics Collection - Uptime Kuma - ${REMOTE_SERVER_HOSTNAME}: icon: uptime-kuma.png - href: https://status.${REMOTE_SERVER_HOSTNAME}.${DOMAIN} + href: https://status.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org description: ${REMOTE_SERVER_HOSTNAME} - Uptime Monitoring - Media: - Jellyfin: icon: jellyfin.png - href: https://jellyfin.${DOMAIN} + href: https://jellyfin.kelinreij.duckdns.org description: Open Source Media Server - Jellyseerr: icon: jellyseerr.png - href: https://jellyseerr.${DOMAIN} + href: https://jellyseerr.kelinreij.duckdns.org description: Media Request Manager - Calibre-Web: icon: calibre-web.png - href: https://calibre.${DOMAIN} + href: https://calibre.kelinreij.duckdns.org description: Ebook Library - Media Management: - Sonarr: icon: sonarr.png - href: https://sonarr.${DOMAIN} + href: https://sonarr.kelinreij.duckdns.org description: TV Shows Automation - Radarr: icon: radarr.png - href: https://radarr.${DOMAIN} + href: https://radarr.kelinreij.duckdns.org description: Movies Automation - Prowlarr: icon: prowlarr.png - href: https://prowlarr.${DOMAIN} + href: https://prowlarr.kelinreij.duckdns.org description: Indexer Manager - Readarr: icon: readarr.png - href: https://readarr.${DOMAIN} + href: https://readarr.kelinreij.duckdns.org description: Books Automation - Lidarr: icon: lidarr.png - href: https://lidarr.${DOMAIN} + href: https://lidarr.kelinreij.duckdns.org description: Music Automation - Mylar3: icon: mylar.png - href: https://mylar.${DOMAIN} + href: https://mylar.kelinreij.duckdns.org description: Comics Manager - Home Automation: - Home Assistant: icon: home-assistant.png - href: https://hass.${DOMAIN} + href: https://hass.kelinreij.duckdns.org description: Home Automation Platform - ESPHome: icon: esphome.png - href: https://esphome.${DOMAIN} + href: https://esphome.kelinreij.duckdns.org description: ESP Device Manager - Node-RED: icon: node-red.png - href: https://nodered.${DOMAIN} + href: https://nodered.kelinreij.duckdns.org description: Flow-based Automation - Zigbee2MQTT: icon: zigbee2mqtt.png - href: https://zigbee.${DOMAIN} + href: https://zigbee.kelinreij.duckdns.org description: Zigbee Bridge - Mosquitto: icon: mosquitto.png - href: https://mqtt.${DOMAIN} + href: https://mqtt.kelinreij.duckdns.org description: MQTT Broker - Productivity: - Nextcloud: icon: nextcloud.png - href: https://nextcloud.${DOMAIN} + href: https://nextcloud.kelinreij.duckdns.org description: Cloud Storage & Collaboration - Gitea: icon: gitea.png - href: https://gitea.${DOMAIN} + href: https://gitea.kelinreij.duckdns.org description: Git Repository - Mealie: icon: mealie.png - href: https://mealie.${DOMAIN} + href: https://mealie.kelinreij.duckdns.org description: Recipe Manager - WordPress: icon: wordpress.png - href: https://wordpress.${DOMAIN} + href: https://wordpress.kelinreij.duckdns.org description: CMS Platform - Wikis: - BookStack: icon: bookstack.png - href: https://bookstack.${DOMAIN} + href: https://bookstack.kelinreij.duckdns.org description: Wiki Platform - DokuWiki: icon: dokuwiki.png - href: https://dokuwiki.${DOMAIN} + href: https://dokuwiki.kelinreij.duckdns.org description: Simple Wiki - Mediawiki: icon: mediawiki.png - href: https://mediawiki.${DOMAIN} + href: https://mediawiki.kelinreij.duckdns.org description: Collaborative Wiki - Development: - VS Code Server: icon: vscode.png - href: https://code.${DOMAIN} + href: https://code.kelinreij.duckdns.org description: Browser-based IDE - Jupyter: icon: jupyter.png - href: https://jupyter.${DOMAIN} + href: https://jupyter.kelinreij.duckdns.org description: Data Science Notebooks - Downloaders: - qBittorrent: icon: qbittorrent.png - href: https://qbit.${DOMAIN} + href: https://qbit.kelinreij.duckdns.org description: Torrent Client - Transcoders: - Tdarr: icon: tdarr.png - href: https://tdarr.${DOMAIN} + href: https://tdarr.kelinreij.duckdns.org description: Media Transcoding - Unmanic: icon: unmanic.png - href: https://unmanic.${DOMAIN} + href: https://unmanic.kelinreij.duckdns.org description: Media Transcoder - Utilities: - Vaultwarden: icon: vaultwarden.png - href: https://vault.${DOMAIN} + href: https://vault.kelinreij.duckdns.org description: Password Manager - Formio: icon: mdi-form-select - href: https://formio.${DOMAIN} + href: https://formio.kelinreij.duckdns.org description: Form Builder - Backup: - Backrest: icon: mdi-backup-restore - href: https://backrest.${DOMAIN} + href: https://backrest.kelinreij.duckdns.org description: Backup Solution - Backrest - ${REMOTE_SERVER_HOSTNAME}: icon: mdi-backup-restore - href: https://backrest.${REMOTE_SERVER_HOSTNAME}.${DOMAIN} + href: https://backrest.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org description: ${REMOTE_SERVER_HOSTNAME} - Backup Solution - Duplicati: icon: duplicati.png - href: https://duplicati.${DOMAIN} + href: https://duplicati.kelinreij.duckdns.org description: Backup Software - Duplicati - ${REMOTE_SERVER_HOSTNAME}: icon: duplicati.png - href: https://duplicati.${REMOTE_SERVER_HOSTNAME}.${DOMAIN} + href: https://duplicati.${REMOTE_SERVER_HOSTNAME}.kelinreij.duckdns.org description: ${REMOTE_SERVER_HOSTNAME} - Backup Software - Metrics: - Grafana: icon: grafana.png - href: https://grafana.${DOMAIN} + href: https://grafana.kelinreij.duckdns.org description: Metrics Dashboard - Prometheus: icon: prometheus.png - href: https://prometheus.${DOMAIN} + href: https://prometheus.kelinreij.duckdns.org description: Metrics Collection - cAdvisor: icon: cadvisor.png - href: https://cadvisor.${DOMAIN} + href: https://cadvisor.kelinreij.duckdns.org description: Container Metrics - Alternatives: - Portainer: icon: portainer.png - href: https://portainer.${DOMAIN} + href: https://portainer.kelinreij.duckdns.org description: Container Management UI - Authentik: icon: authentik.png - href: https://authentik.${DOMAIN} + href: https://authentik.kelinreij.duckdns.org description: Alternative Auth Provider - Plex: icon: plex.png - href: https://plex.${DOMAIN} + href: https://plex.kelinreij.duckdns.org description: Media Server diff --git a/markup.yml b/markup.yml new file mode 100644 index 0000000..7520ba4 --- /dev/null +++ b/markup.yml @@ -0,0 +1,60 @@ + + +echo "╔═════════════════════════════════════════════════════════════╗" +echo "║ EZ-HOMELAB SETUP & DEPLOYMENT ║" +echo "║ ║" +echo "║ 1) Install Docker ║" +echo "║ 2) Deploy Core Server ║" +echo "║ 3) Deploy Additional Server ║" +echo "║ 4) Install NVIDIA Drivers ║" +echo "║ ║" +echo "║ q) Quit ║" +echo "╚═════════════════════════════════════════════════════════════╝" + + +echo "╔═════════════════════════════════════════════════════════════╗ +echo "║ ✅ SERVER_IP: 192.168.4.4 ║ +echo "║ ✅ SERVER_HOSTNAME: jasper ║ +echo "║ ✅ DUCKDNS_SUBDOMAINS: kelinreij ║ +echo "║ ✅ DUCKDNS_TOKEN: 41ef7faa-fc93-41d2-a32f-340fd2b75b2f ║ +echo "║ ✅ DOMAIN: kelinreij.duckdns.org ║ +echo "║ ✅ DEFAULT_USER: kelin ║ +echo "║ ✅ DEFAULT_PASSWORD: Tiberi0u$ ║ +echo "║ ✅ DEFAULT_EMAIL: kelinshomelab@gmail.com ║ +echo "╚═════════════════════════════════════════════════════════════╝ + +echo "╔═════════════════════════════════════════════════════════════╗ +echo "║ Use These Values ? (Y/n) ║ +echo "╚═════════════════════════════════════════════════════════════╝ + +echo "╔═════════════════════════════════════════════════════════════╗ +echo "║ Deployment Complete! ║ +echo "║ SSL Certificates may take a few minutes to be issued. ║ +echo "║ ║ +echo "║ https://dockge.kelinreij.duckdns.org ║ +echo "║ http://192.168.4.4:5001 ║ +echo "║ ║ +echo "║ https://homepage.kelinreij.duckdns.org ║ +echo "║ http://192.168.4.4:3003 ║ +echo "║ ║ +echo "║ https://authelia.kelinreij.duckdns.org ║ +echo "║ http://192.168.4.4:9091 ║ +echo "║ ║ +echo "║ https://traefik.kelinreij.duckdns.org ║ +echo "║ http://192.168.4.4:8080 ║ +echo "║ ║ +echo "╚═════════════════════════════════════════════════════════════╝ + +echo "╔═════════════════════════════════════════════════════════════╗ +echo "║ ⚠️ WARNING ⚠️ ║ +echo "║ The following variables were not defined ║ +echo "║ If something isn't working as expected check these first ║ +echo "║ ║ +echo "║ WATCHTOWER_NOTIFICATION_URL WATCHTOWER_NOTIFICATION_URL ║ +echo "║ AUTHENTIK_DB_NAME AUTHENTIK_DB_PASSWORD ║ +echo "║ ║ +echo "╚═════════════════════════════════════════════════════════════╝ + + + + diff --git a/scripts/ez-homelab.sh b/scripts/ez-homelab.sh index 17d41d3..dd784ee 100755 --- a/scripts/ez-homelab.sh +++ b/scripts/ez-homelab.sh @@ -8,6 +8,7 @@ set -e # Exit on error # Debug logging configuration DEBUG=${DEBUG:-false} +VERBOSE=${VERBOSE:-false} # New verbosity toggle DEBUG_LOG_FILE="/tmp/ez-homelab-debug.log" # Colors for output @@ -33,17 +34,23 @@ fi # Log functions log_info() { - echo -e "${BLUE}[INFO]${NC} $1" + if [ "$VERBOSE" = true ]; then + echo -e "${BLUE}[INFO]${NC} $1" + fi debug_log "[INFO] $1" } log_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" + if [ "$VERBOSE" = true ]; then + echo -e "${GREEN}[SUCCESS]${NC} $1" + fi debug_log "[SUCCESS] $1" } log_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" + if [ "$VERBOSE" = true ]; then + echo -e "${YELLOW}[WARNING]${NC} $1" + fi debug_log "[WARNING] $1" } @@ -107,6 +114,14 @@ replace_env_placeholders() { debug_log "Found variables to replace: $vars" for var in $vars; do + # Skip derived variables that should not be replaced + case "$var" in + "ACME_EMAIL"|"AUTHELIA_ADMIN_EMAIL"|"SMTP_USERNAME"|"SMTP_PASSWORD") + debug_log "Skipping derived variable: $var" + continue + ;; + esac + if [ -z "${!var:-}" ]; then log_warning "Environment variable $var not found in .env file" debug_log "Missing variable: $var" @@ -132,6 +147,38 @@ replace_env_placeholders() { fi } +# Enhanced placeholder replacement for all configuration files +enhance_placeholder_replacement() { + log_info "Starting enhanced placeholder replacement..." + + local processed_files=0 + + # Process docker-compose files + if [ -d "$REPO_DIR/docker-compose" ]; then + while IFS= read -r -d '' file_path; do + if [ -f "$file_path" ]; then + debug_log "Processing docker-compose file: $file_path" + replace_env_placeholders "$file_path" false + processed_files=$((processed_files + 1)) + fi + done < <(find "$REPO_DIR/docker-compose" -name "*.yml" -o -name "*.yaml" -print0 2>/dev/null) + fi + + # Process config-templates files + if [ -d "$REPO_DIR/config-templates" ]; then + while IFS= read -r -d '' file_path; do + if [ -f "$file_path" ]; then + debug_log "Processing config template file: $file_path" + replace_env_placeholders "$file_path" false + processed_files=$((processed_files + 1)) + fi + done < <(find "$REPO_DIR/config-templates" -name "*.yml" -o -name "*.yaml" -print0 2>/dev/null) + fi + + log_success "Enhanced placeholder replacement completed - processed $processed_files files" + debug_log "Enhanced replacement completed for $processed_files files" +} + # Function to generate shared CA for multi-server TLS generate_shared_ca() { local ca_dir="/opt/stacks/core/shared-ca" @@ -296,35 +343,14 @@ DEPLOY_DASHBOARDS=false SETUP_STACKS=false TLS_ISSUES_SUMMARY="" +# Required variables for configuration +REQUIRED_VARS=("SERVER_IP" "SERVER_HOSTNAME" "DUCKDNS_SUBDOMAINS" "DUCKDNS_TOKEN" "DOMAIN" "DEFAULT_USER" "DEFAULT_PASSWORD" "DEFAULT_EMAIL") + # Load existing .env file if it exists load_env_file() { if [ -f "$REPO_DIR/.env" ]; then log_info "Found existing .env file, loading current configuration..." load_env_file_safely "$REPO_DIR/.env" - - # Show current values - echo "" - echo "Current configuration:" - echo " Domain: ${DOMAIN:-Not set}" - echo " Server IP: ${SERVER_IP:-Not set}" - echo " Server Hostname: ${SERVER_HOSTNAME:-Not set}" - echo " Remote Server IP: ${REMOTE_SERVER_IP:-Not set}" - echo " Remote Server Hostname: ${REMOTE_SERVER_HOSTNAME:-Not set}" - echo " Remote Server User: ${REMOTE_SERVER_USER:-Not set}" - if [ -n "${REMOTE_SERVER_PASSWORD:-}" ]; then - echo " Remote Server Password: [HIDDEN]" - else - echo " Remote Server Password: Not set" - fi - echo " Default User: ${DEFAULT_USER:-Not set}" - if [ -n "${DEFAULT_PASSWORD:-}" ]; then - echo " Default Password: [HIDDEN]" - else - echo " Default Password: Not set" - fi - echo " Timezone: ${TZ:-Not set}" - echo "" - return 0 else log_info "No existing .env file found. We'll create one during setup." @@ -332,6 +358,256 @@ load_env_file() { fi } +# Validate variable values +validate_variable() { + local var_name="$1" + local var_value="$2" + + case "$var_name" in + "SERVER_IP") + # Basic IP validation + if [[ $var_value =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + return 0 + else + return 1 + fi + ;; + "DOMAIN") + # Basic domain validation + if [[ $var_value =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then + return 0 + else + return 1 + fi + ;; + "DUCKDNS_SUBDOMAINS") + # DuckDNS subdomain should be non-empty and contain only valid characters + local trimmed_value=$(echo "$var_value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + if [ -n "$trimmed_value" ] && [[ $trimmed_value =~ ^[a-zA-Z0-9.-]+$ ]]; then + return 0 + else + return 1 + fi + ;; + "DEFAULT_PASSWORD") + # Password should be at least 8 characters + if [ ${#var_value} -ge 8 ]; then + return 0 + else + return 1 + fi + ;; + "DEFAULT_EMAIL") + # Basic email validation + if [[ $var_value =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then + return 0 + else + return 1 + fi + ;; + *) + # For other variables, trim whitespace and check they're not empty + local trimmed_value=$(echo "$var_value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + if [ -n "$trimmed_value" ]; then + return 0 + else + return 1 + fi + ;; + esac +} + +# Prompt for a single variable +prompt_for_variable() { + local var="$1" + local user_input="" + local current_value="${!var:-}" + local prompt_text="" + + while true; do + # Build prompt text with current value if it exists + if [ -n "$current_value" ]; then + if [ "$var" = "DEFAULT_PASSWORD" ]; then + prompt_text="🔒 ${var} ([HIDDEN]): " + else + prompt_text="${var} (${current_value}): " + fi + else + prompt_text="${var}: " + fi + + # Add icon prefix + case "$var" in + "SERVER_IP") + prompt_text="🌐 ${prompt_text}" + ;; + "DOMAIN") + prompt_text="🌍 ${prompt_text}" + ;; + "DUCKDNS_SUBDOMAINS") + prompt_text="🦆 ${prompt_text}" + ;; + "DUCKDNS_TOKEN") + prompt_text="🔑 ${prompt_text}" + ;; + "DEFAULT_USER") + prompt_text="👤 ${prompt_text}" + ;; + "DEFAULT_PASSWORD") + # Lock icon already added above for passwords + ;; + "DEFAULT_EMAIL") + prompt_text="📧 ${prompt_text}" + ;; + "SERVER_HOSTNAME") + prompt_text="🏠 ${prompt_text}" + ;; + esac + + # Get user input + if [ "$var" = "DEFAULT_PASSWORD" ]; then + read -s -p "$prompt_text" user_input + echo "" + else + read -p "$prompt_text" user_input + fi + + # Check for quit command + if [ "$user_input" = "q" ] || [ "$user_input" = "Q" ]; then + log_info "Setup cancelled by user" + exit 0 + fi + + if [ -z "$user_input" ]; then + if [ -n "$current_value" ]; then + # Use existing value - overwrite prompt with status + if [ "$var" != "DEFAULT_PASSWORD" ]; then + echo -e "\033[1A\033[K✅ ${var}: ${current_value}" + fi + return 0 + else + log_warning "${var} cannot be empty. Please provide a value." + continue + fi + fi + + if validate_variable "$var" "$user_input"; then + eval "$var=\"$user_input\"" + # Overwrite prompt with status + if [ "$var" != "DEFAULT_PASSWORD" ]; then + echo -e "\033[1A\033[K✅ ${var}: ${user_input}" + else + echo -e "\033[1A\033[K✅ ${var}: [HIDDEN]" + fi + return 0 + else + log_warning "Invalid value for ${var}. Please try again." + continue + fi + done +} + +# Validate and prompt for required variables with loop +validate_and_prompt_variables() { + local all_valid=false + local user_wants_to_review=false + local first_display=true + + while true; do + user_wants_to_review=false + + all_valid=true + + # Check validity without showing initial summary + for var in "${REQUIRED_VARS[@]}"; do + local display_value=$(echo "${!var:-}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + if [ -z "$display_value" ] || ! validate_variable "$var" "${!var}"; then + all_valid=false + fi + done + + if [ "$all_valid" = true ]; then + if [ "$first_display" = true ]; then + echo "Current configuration:" + for var in "${REQUIRED_VARS[@]}"; do + local display_value=$(echo "${!var:-}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + if [ "$var" = "DEFAULT_PASSWORD" ]; then + echo " ✅ ${var}: [HIDDEN]" + else + echo " ✅ ${var}: ${display_value}" + fi + done + echo "" + first_display=false + fi + echo "" + echo "Choose an option:" + echo " 1) ✅ Deploy now" + echo " 2) 🔄 Make Changes" + echo " q) ❌ Quit setup" + echo "" + read -p "Enter your choice (1, 2, or q): " user_choice + + case "$user_choice" in + 1|"p"|"proceed") + log_info "Proceeding with current configuration..." + return 0 + ;; + 2|"r"|"review"|"change") + user_wants_to_review=true + echo "" + echo "Reviewing all variables - press Enter to keep current value or enter new value:" + echo "" + ;; + [Qq]|[Qq]uit) + log_info "Setup cancelled by user" + exit 0 + ;; + *) + log_warning "Invalid choice. Please enter 1, 2, or q." + echo "" + continue + ;; + esac + else + echo "" + echo "Some variables need configuration. Press Enter to continue or 'q' to quit." + read -p "Press Enter to configure missing variables, or 'q' to quit: " user_choice + + case "$user_choice" in + [Qq]|[Qq]uit) + log_info "Setup cancelled by user" + exit 0 + ;; + ""|"c"|"continue") + # Continue with prompting + ;; + *) + log_warning "Invalid choice. Press Enter to continue or 'q' to quit." + continue + ;; + esac + fi + + # Prompt for variables (either missing ones or all if reviewing) + if [ "$user_wants_to_review" = true ]; then + # Review all variables one by one + for var in "${REQUIRED_VARS[@]}"; do + prompt_for_variable "$var" + done + # After review, continue the loop to show menu again + continue + else + # Only prompt for missing/invalid variables + for var in "${REQUIRED_VARS[@]}"; do + if [ -z "${!var:-}" ] || ! validate_variable "$var" "${!var}"; then + prompt_for_variable "$var" + fi + done + fi + done +} + # Save configuration to .env file save_env_file() { debug_log "save_env_file() called, DEPLOY_CORE=$DEPLOY_CORE" @@ -342,15 +618,20 @@ save_env_file() { sudo -u "$ACTUAL_USER" cp "$REPO_DIR/.env.example" "$REPO_DIR/.env" fi - # Update values as the actual user - sudo -u "$ACTUAL_USER" sed -i "s%DOMAIN=.*%DOMAIN=$DOMAIN%" "$REPO_DIR/.env" - sudo -u "$ACTUAL_USER" sed -i "s%SERVER_IP=.*%SERVER_IP=$SERVER_IP%" "$REPO_DIR/.env" - sudo -u "$ACTUAL_USER" sed -i "s%SERVER_HOSTNAME=.*%SERVER_HOSTNAME=$SERVER_HOSTNAME%" "$REPO_DIR/.env" - sudo -u "$ACTUAL_USER" sed -i "s%REMOTE_SERVER_IP=.*%REMOTE_SERVER_IP=$REMOTE_SERVER_IP%" "$REPO_DIR/.env" - sudo -u "$ACTUAL_USER" sed -i "s%REMOTE_SERVER_HOSTNAME=.*%REMOTE_SERVER_HOSTNAME=$REMOTE_SERVER_HOSTNAME%" "$REPO_DIR/.env" - sudo -u "$ACTUAL_USER" sed -i "s%REMOTE_SERVER_USER=.*%REMOTE_SERVER_USER=$REMOTE_SERVER_USER%" "$REPO_DIR/.env" - sudo -u "$ACTUAL_USER" sed -i "s%REMOTE_SERVER_PASSWORD=.*%REMOTE_SERVER_PASSWORD=$REMOTE_SERVER_PASSWORD%" "$REPO_DIR/.env" - sudo -u "$ACTUAL_USER" sed -i "s%TZ=.*%TZ=$TZ%" "$REPO_DIR/.env" + # Update only the required variables + for var in "${REQUIRED_VARS[@]}"; do + if [ -n "${!var:-}" ]; then + sudo -u "$ACTUAL_USER" sed -i "s|^${var}=.*|${var}=${!var}|" "$REPO_DIR/.env" + fi + done + + # Update HOMEPAGE_ALLOWED_HOSTS dynamically + if [ -n "${DOMAIN:-}" ] && [ -n "${SERVER_IP:-}" ]; then + # Allow user to specify homepage subdomain + HOMEPAGE_SUBDOMAIN="${HOMEPAGE_SUBDOMAIN:-homepage}" + HOMEPAGE_ALLOWED_HOSTS="${HOMEPAGE_SUBDOMAIN}.${DOMAIN},${SERVER_IP}:3003" + sudo -u "$ACTUAL_USER" sed -i "s|HOMEPAGE_ALLOWED_HOSTS=.*|HOMEPAGE_ALLOWED_HOSTS=$HOMEPAGE_ALLOWED_HOSTS|" "$REPO_DIR/.env" + fi # Authelia settings (only generate secrets if deploying core) if [ "$DEPLOY_CORE" = true ]; then @@ -471,141 +752,6 @@ validate_secrets() { debug_log "Secret validation passed" } -# Prompt for required values -prompt_for_values() { - echo "" - log_info "Configuration Setup:" - echo "" - - # Set defaults from env file or hardcoded fallbacks - DEFAULT_DOMAIN="${DOMAIN:-example.duckdns.org}" - DEFAULT_SERVER_IP="${SERVER_IP:-$(hostname -I | awk '{print $1}')}" - DEFAULT_CORE_SERVER_IP="${CORE_SERVER_IP:-}" - DEFAULT_SERVER_HOSTNAME="${SERVER_HOSTNAME:-$(hostname)}" - DEFAULT_REMOTE_SERVER_IP="${REMOTE_SERVER_IP:-}" - DEFAULT_REMOTE_SERVER_HOSTNAME="${REMOTE_SERVER_HOSTNAME:-}" - DEFAULT_REMOTE_SERVER_USER="${REMOTE_SERVER_USER:-${DEFAULT_USER}}" - DEFAULT_REMOTE_SERVER_PASSWORD="${REMOTE_SERVER_PASSWORD:-}" - DEFAULT_TZ="${TZ:-America/New_York}" - - # Display current/default configuration - echo "Please review the following configuration:" - echo " Domain: $DEFAULT_DOMAIN" - echo " Server IP: $DEFAULT_SERVER_IP" - echo " Server Hostname: $DEFAULT_SERVER_HOSTNAME" - echo " Remote Server IP: $DEFAULT_REMOTE_SERVER_IP" - echo " Remote Server Hostname: $DEFAULT_REMOTE_SERVER_HOSTNAME" - echo " Remote Server User: $DEFAULT_REMOTE_SERVER_USER" - if [ -n "$DEFAULT_REMOTE_SERVER_PASSWORD" ]; then - echo " Remote Server Password: [HIDDEN]" - else - echo " Remote Server Password: Not set" - fi - echo " Timezone: $DEFAULT_TZ" - - if [ "$DEPLOY_CORE" = false ] && [ -z "$DEFAULT_CORE_SERVER_IP" ]; then - echo " Core Server IP: [Will be prompted for multi-server TLS]" - elif [ -n "$DEFAULT_CORE_SERVER_IP" ]; then - echo " Core Server IP: $DEFAULT_CORE_SERVER_IP" - fi - - if [ "$DEPLOY_CORE" = true ]; then - DEFAULT_ADMIN_USER="${DEFAULT_USER:-admin}" - DEFAULT_ADMIN_EMAIL="${DEFAULT_EMAIL:-${DEFAULT_ADMIN_USER}@${DEFAULT_DOMAIN}}" - echo " Admin User: $DEFAULT_ADMIN_USER" - echo " Admin Email: $DEFAULT_ADMIN_EMAIL" - echo " Admin Password: [Will be prompted if needed]" - fi - - echo "" - read -p "Use these default values? (Y/n): " -n 1 -r - echo "" - if [[ $REPLY =~ ^[Nn]$ ]]; then - echo "Please enter custom values:" - echo "" - - # Domain - read -p "Domain [$DEFAULT_DOMAIN]: " DOMAIN - DOMAIN="${DOMAIN:-$DEFAULT_DOMAIN}" - - # Server IP - read -p "Server IP [$DEFAULT_SERVER_IP]: " SERVER_IP - SERVER_IP="${SERVER_IP:-$DEFAULT_SERVER_IP}" - - # Server Hostname - read -p "Server Hostname [$DEFAULT_SERVER_HOSTNAME]: " SERVER_HOSTNAME - SERVER_HOSTNAME="${SERVER_HOSTNAME:-$DEFAULT_SERVER_HOSTNAME}" - - # Remote Server IP - read -p "Remote Server IP [$DEFAULT_REMOTE_SERVER_IP]: " REMOTE_SERVER_IP - REMOTE_SERVER_IP="${REMOTE_SERVER_IP:-$DEFAULT_REMOTE_SERVER_IP}" - - # Remote Server Hostname - read -p "Remote Server Hostname [$DEFAULT_REMOTE_SERVER_HOSTNAME]: " REMOTE_SERVER_HOSTNAME - REMOTE_SERVER_HOSTNAME="${REMOTE_SERVER_HOSTNAME:-$DEFAULT_REMOTE_SERVER_HOSTNAME}" - - # Remote Server User - read -p "Remote Server User [$DEFAULT_REMOTE_SERVER_USER]: " REMOTE_SERVER_USER - REMOTE_SERVER_USER="${REMOTE_SERVER_USER:-$DEFAULT_REMOTE_SERVER_USER}" - - # Remote Server Password - read -s -p "Remote Server Password: " REMOTE_SERVER_PASSWORD - echo "" - if [ -z "$REMOTE_SERVER_PASSWORD" ]; then - REMOTE_SERVER_PASSWORD="$DEFAULT_REMOTE_SERVER_PASSWORD" - fi - - # Timezone - read -p "Timezone [$DEFAULT_TZ]: " TZ - TZ="${TZ:-$DEFAULT_TZ}" - - # Core server IP (for multi-server setup) - if [ "$DEPLOY_CORE" = false ]; then - echo "" - read -p "Core server IP (for shared TLS CA): " CORE_SERVER_IP - fi - - # Admin credentials (only if deploying core) - if [ "$DEPLOY_CORE" = true ]; then - echo "" - log_info "Authelia Admin Credentials:" - - read -p "Admin username [$DEFAULT_ADMIN_USER]: " ADMIN_USER - ADMIN_USER="${ADMIN_USER:-$DEFAULT_ADMIN_USER}" - - read -p "Admin email [$DEFAULT_ADMIN_EMAIL]: " ADMIN_EMAIL - ADMIN_EMAIL="${ADMIN_EMAIL:-$DEFAULT_ADMIN_EMAIL}" - - if [ -z "$ADMIN_PASSWORD" ]; then - while [ -z "$ADMIN_PASSWORD" ]; do - read -s -p "Admin password (will be hashed): " ADMIN_PASSWORD - echo "" - if [ ${#ADMIN_PASSWORD} -lt 8 ]; then - log_warning "Password must be at least 8 characters" - ADMIN_PASSWORD="" - fi - done - else - log_info "Admin password already configured" - fi - fi - else - # Use defaults - DOMAIN="$DEFAULT_DOMAIN" - SERVER_IP="$DEFAULT_SERVER_IP" - SERVER_HOSTNAME="$DEFAULT_SERVER_HOSTNAME" - TZ="$DEFAULT_TZ" - CORE_SERVER_IP="$DEFAULT_CORE_SERVER_IP" - - if [ "$DEPLOY_CORE" = true ]; then - ADMIN_USER="$DEFAULT_ADMIN_USER" - ADMIN_EMAIL="$DEFAULT_ADMIN_EMAIL" - fi - fi - - echo "" -} - # System setup function (Docker, directories, etc.) system_setup() { log_info "Performing system setup..." @@ -1057,13 +1203,7 @@ perform_deployment() { log_info "Please update your .env file and redeploy affected stacks." fi - # Report any TLS issues - if [ -n "$TLS_ISSUES_SUMMARY" ]; then - echo "" - log_warning "TLS Configuration Issues Detected:" - echo "$TLS_ISSUES_SUMMARY" - echo "" - fi + # TLS issues will be reported in the final summary } # Setup Docker TLS function @@ -1186,28 +1326,18 @@ setup_stacks_for_dockge() { # Main menu show_main_menu() { - echo "==========================================" - echo " EZ-HOMELAB SETUP & DEPLOYMENT" - echo "==========================================" + clear echo "" - echo "What would you like to do?" - echo "" - echo "1) 🚀 Default Setup (Recommended)" - echo " - Deploy Dockge, core infrastructure, dashboards & monitoring" - echo " - All additional stacks prepared for Dockge" - echo "" - echo "2) 🏗️ Core Only" - echo " - Deploy Dockge and core infrastructure only" - echo " - All stacks prepared for Dockge" - echo "" - echo "3) 🔧 Infrastructure Only" - echo " - Deploy Dockge and monitoring tools" - echo " - Requires existing Traefik (from previous setup)" - echo " - Configures TLS for remote Docker access (Sablier)" - echo " - Services accessible without authentication" - echo " - All stacks prepared for Dockge" - echo "" - echo "4) ❌ Exit" + echo "╔═════════════════════════════════════════════════════════════╗" + echo "║ EZ-HOMELAB SETUP & DEPLOYMENT ║" + echo "║ ║" + echo "║ 1) Install Prerequisites ║" + echo "║ 2) Deploy Core Server ║" + echo "║ 3) Deploy Additional Server ║" + echo "║ 4) Install NVIDIA Drivers ║" + echo "║ ║" + echo "║ q) Quit ║" + echo "╚═════════════════════════════════════════════════════════════╝" echo "" } @@ -1228,31 +1358,24 @@ main() { # Show main menu show_main_menu - read -p "Choose an option (1-4): " MAIN_CHOICE + read -p "Choose an option (1-3): " MAIN_CHOICE case $MAIN_CHOICE in 1) - log_info "Selected: Default Setup" + log_info "Selected: Core Server" DEPLOY_CORE=true DEPLOY_INFRASTRUCTURE=true DEPLOY_DASHBOARDS=true SETUP_STACKS=true ;; 2) - log_info "Selected: Core Only" - DEPLOY_CORE=true - DEPLOY_INFRASTRUCTURE=false - DEPLOY_DASHBOARDS=true - SETUP_STACKS=true - ;; - 3) - log_info "Selected: Infrastructure Only" + log_info "Selected: Additional Server" DEPLOY_CORE=false DEPLOY_INFRASTRUCTURE=true DEPLOY_DASHBOARDS=false SETUP_STACKS=true ;; - 4) + 3) log_info "Exiting..." exit 0 ;; @@ -1316,11 +1439,14 @@ main() { log_success "Directories ready" # Prompt for configuration values - prompt_for_values + validate_and_prompt_variables # Save configuration save_env_file + # Perform enhanced placeholder replacement across all config files + enhance_placeholder_replacement + # Validate secrets for core deployment validate_secrets @@ -1329,36 +1455,49 @@ main() { # Show completion message echo "" - echo "==========================================" - log_success "Setup and deployment completed successfully!" - echo "==========================================" - echo "" + echo "╔═════════════════════════════════════════════════════════════╗" + echo "║ Deployment Complete! ║" + echo "║ SSL Certificates may take a few minutes to be issued. ║" + echo "║ ║" + echo "║ https://dockge.kelinreij.duckdns.org ║" + echo "║ http://192.168.4.4:5001 ║" + echo "║ ║" + echo "║ https://homepage.kelinreij.duckdns.org ║" + echo "║ http://192.168.4.4:3003 ║" + echo "║ ║" + echo "╚═════════════════════════════════════════════════════════════╝" - if [ "$DEPLOY_INFRASTRUCTURE" = true ]; then - log_info "Access your services:" - echo "" - echo " 🚀 Dockge: https://dockge.${DOMAIN}" - [ "$DEPLOY_CORE" = true ] && echo " 🔒 Authelia: https://auth.${DOMAIN}" - [ "$DEPLOY_CORE" = true ] && echo " 🔀 Traefik: https://traefik.${DOMAIN}" - echo " 📊 Homepage: https://homepage.${DOMAIN}" - echo "" + # Show consolidated warnings if any + if [ -n "$MISSING_VARS_SUMMARY" ] || [ -n "$TLS_ISSUES_SUMMARY" ]; then + echo "╔═════════════════════════════════════════════════════════════╗" + echo "║ ⚠️ WARNING ⚠️ ║" + echo "║ The following variables were not defined ║" + echo "║ If something isn't working as expected check these first ║" + echo "║ ║" + + if [ -n "$MISSING_VARS_SUMMARY" ]; then + log_warning "Missing Environment Variables:" + echo "$MISSING_VARS_SUMMARY" + echo "║ ║" + fi + + if [ -n "$TLS_ISSUES_SUMMARY" ]; then + log_warning "TLS Configuration Issues:" + echo "$TLS_ISSUES_SUMMARY" + echo "║ ║" + fi fi + echo "╚═════════════════════════════════════════════════════════════╝" - log_info "Next steps:" + echo "╔══════════════════════════════════════════╗" + echo "║ 📚 RESOURCES ║" + echo "╚══════════════════════════════════════════╝" echo "" - echo " 1. Access Dockge at https://dockge.${DOMAIN}" - if [ "$DEPLOY_CORE" = true ]; then - echo " (Use your Authelia credentials: ${AUTHELIA_ADMIN_USER})" - fi + echo " 📖 Documentation: $REPO_DIR/docs/" + echo " 🔧 Quick Reference: $REPO_DIR/docs/quick-reference.md" + echo " 🐙 Repository: https://github.com/your-repo/ez-homelab" + echo " 📋 Wiki: https://github.com/your-repo/ez-homelab/wiki" echo "" - echo " 2. Start additional stacks from Dockge's web UI" - echo "" - echo " 3. Configure services via the AI assistant in VS Code" - echo "" - echo "==========================================" - echo "" - log_info "For documentation, see: $REPO_DIR/docs/" - log_info "For troubleshooting, see: $REPO_DIR/docs/quick-reference.md" debug_log "Script completed successfully" echo "" }