mirror of
https://github.com/kossakovsky/n8n-install.git
synced 2026-03-07 22:33:11 +00:00
refactor(utils): improve whiptail dialogs and logging utilities
- add adaptive terminal sizing for all whiptail dialogs (wt_get_size) - add new wrapper functions: wt_checklist, wt_radiolist, wt_menu - add safe parser wt_parse_choices to replace eval - improve NEWT_COLORS theme with better contrast (brightgreen/brightcyan) - add new logging functions: log_header, log_subheader, log_divider, log_box - add spinner animation utilities for progress indication - expand color palette with bright variants and text styles - update 04_wizard.sh to use new whiptail wrappers
This commit is contained in:
@@ -21,10 +21,12 @@ init_paths
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# System Update
|
||||
log_subheader "System Update"
|
||||
log_info "Updating package list and upgrading the system..."
|
||||
apt update -y && apt upgrade -y
|
||||
|
||||
# Installing Basic Utilities
|
||||
log_subheader "Installing Utilities"
|
||||
log_info "Installing standard CLI tools..."
|
||||
apt install -y \
|
||||
htop git curl make unzip ufw fail2ban python3 psmisc whiptail \
|
||||
@@ -32,7 +34,8 @@ apt install -y \
|
||||
debian-keyring debian-archive-keyring apt-transport-https python3-pip python3-dotenv python3-yaml
|
||||
|
||||
# Configuring Firewall (UFW)
|
||||
log_info "Configuring firewall (UFW)..."
|
||||
log_subheader "Firewall (UFW)"
|
||||
log_info "Configuring firewall..."
|
||||
echo "y" | ufw reset
|
||||
ufw --force enable
|
||||
ufw default deny incoming
|
||||
@@ -44,7 +47,8 @@ ufw reload
|
||||
ufw status
|
||||
|
||||
# Configuring Fail2Ban
|
||||
log_info "Enabling brute-force protection (Fail2Ban)..."
|
||||
log_subheader "Fail2Ban"
|
||||
log_info "Enabling brute-force protection..."
|
||||
systemctl enable fail2ban
|
||||
sleep 1
|
||||
systemctl start fail2ban
|
||||
@@ -54,12 +58,14 @@ sleep 1
|
||||
fail2ban-client status sshd
|
||||
|
||||
# Automatic Security Updates
|
||||
log_subheader "Security Updates"
|
||||
log_info "Enabling automatic security updates..."
|
||||
apt install -y unattended-upgrades
|
||||
# Automatic confirmation for dpkg-reconfigure
|
||||
echo "y" | dpkg-reconfigure --priority=low unattended-upgrades
|
||||
|
||||
# Configure vm.max_map_count for Elasticsearch (required for RAGFlow)
|
||||
log_subheader "Kernel Parameters"
|
||||
log_info "Configuring vm.max_map_count for Elasticsearch..."
|
||||
CURRENT_VALUE=$(sysctl -n vm.max_map_count 2>/dev/null || echo "0")
|
||||
if [[ "$CURRENT_VALUE" -lt 262144 ]]; then
|
||||
|
||||
@@ -21,7 +21,6 @@ source "$(dirname "$0")/utils.sh"
|
||||
# 1. Preparing the environment
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
APT_OPTIONS="-o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef -y"
|
||||
log_info "Preparing Docker installation..."
|
||||
|
||||
# Configuration for apt retry logic
|
||||
APT_RETRY_COUNT=10
|
||||
@@ -62,6 +61,7 @@ run_apt_with_retry() {
|
||||
|
||||
|
||||
# Check if Docker is already installed
|
||||
log_subheader "Docker Check"
|
||||
if command -v docker &> /dev/null; then
|
||||
log_info "Docker is already installed."
|
||||
docker --version
|
||||
@@ -92,6 +92,7 @@ if command -v docker &> /dev/null; then
|
||||
fi
|
||||
|
||||
# 2. Updating and installing dependencies
|
||||
log_subheader "Dependencies"
|
||||
log_info "Installing necessary dependencies..."
|
||||
run_apt_with_retry update -qq
|
||||
run_apt_with_retry install -qq $APT_OPTIONS \
|
||||
@@ -101,19 +102,21 @@ run_apt_with_retry install -qq $APT_OPTIONS \
|
||||
lsb-release || { log_error "Failed to install dependencies."; exit 1; }
|
||||
|
||||
# 3. Adding Docker's GPG key
|
||||
log_subheader "Docker Repository"
|
||||
log_info "Adding Docker's GPG key..."
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
|
||||
# 4. Adding the Docker repository
|
||||
log_info "Adding the official Docker repository..."
|
||||
log_info "Adding the official Docker APT repository..."
|
||||
echo \
|
||||
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
|
||||
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
||||
tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
|
||||
# 5. Installing Docker and Docker Compose
|
||||
log_subheader "Docker Installation"
|
||||
log_info "Installing Docker Engine and Compose Plugin..."
|
||||
run_apt_with_retry update -qq
|
||||
run_apt_with_retry install -qq $APT_OPTIONS \
|
||||
@@ -124,7 +127,7 @@ run_apt_with_retry install -qq $APT_OPTIONS \
|
||||
docker-compose-plugin || { log_error "Failed to install Docker packages."; exit 1; }
|
||||
|
||||
# 6. Adding the user to the Docker group
|
||||
# Use SUDO_USER to get the original user who invoked sudo
|
||||
log_subheader "User Configuration"
|
||||
ORIGINAL_USER=${SUDO_USER:-$(whoami)}
|
||||
log_info "Adding user '$ORIGINAL_USER' to the docker group..."
|
||||
if id "$ORIGINAL_USER" &>/dev/null; then
|
||||
|
||||
@@ -118,6 +118,8 @@ if [ -f "$OUTPUT_FILE" ]; then
|
||||
fi
|
||||
|
||||
# Install Caddy
|
||||
log_subheader "Installing Caddy"
|
||||
log_info "Adding Caddy repository and installing..."
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --yes --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
|
||||
apt install -y caddy
|
||||
@@ -126,7 +128,9 @@ apt install -y caddy
|
||||
require_command "caddy" "Caddy installation failed. Please check the installation logs above."
|
||||
|
||||
require_whiptail
|
||||
|
||||
# Prompt for the domain name
|
||||
log_subheader "Domain Configuration"
|
||||
DOMAIN="" # Initialize DOMAIN variable
|
||||
|
||||
# Try to get domain from existing .env file first
|
||||
@@ -162,6 +166,7 @@ else
|
||||
fi
|
||||
|
||||
# Prompt for user email
|
||||
log_subheader "Email Configuration"
|
||||
if [[ -z "${existing_env_vars[LETSENCRYPT_EMAIL]}" ]]; then
|
||||
wt_msg "Email Required" "Please enter your email address. It will be used for logins and Let's Encrypt SSL."
|
||||
fi
|
||||
@@ -190,6 +195,7 @@ fi
|
||||
|
||||
|
||||
|
||||
log_subheader "Secret Generation"
|
||||
log_info "Generating secrets and creating .env file..."
|
||||
|
||||
# --- Helper Functions ---
|
||||
|
||||
@@ -98,18 +98,16 @@ while [ $idx -lt ${#base_services_data[@]} ]; do
|
||||
idx=$((idx + 2))
|
||||
done
|
||||
|
||||
# Use whiptail to display the checklist
|
||||
num_services=$(( ${#services[@]} / 3 ))
|
||||
CHOICES=$(whiptail --title "Service Selection Wizard" --checklist \
|
||||
"Choose the services you want to deploy.\nUse ARROW KEYS to navigate, SPACEBAR to select/deselect, ENTER to confirm." 32 90 $num_services \
|
||||
"${services[@]}" \
|
||||
3>&1 1>&2 2>&3)
|
||||
# Use whiptail to display the checklist (with adaptive sizing)
|
||||
CHOICES=$(wt_checklist "Service Selection Wizard" \
|
||||
"Choose the services you want to deploy.\nUse ARROW KEYS to navigate, SPACEBAR to select/deselect, ENTER to confirm." \
|
||||
"${services[@]}")
|
||||
exitstatus=$?
|
||||
|
||||
# Restore original DEBIAN_FRONTEND
|
||||
restore_debian_frontend
|
||||
|
||||
# Exit if user pressed Cancel or Esc
|
||||
exitstatus=$?
|
||||
if [ $exitstatus -ne 0 ]; then
|
||||
log_info "Service selection cancelled by user. Exiting wizard."
|
||||
log_info "No changes made to service profiles. Default services will be used."
|
||||
@@ -124,10 +122,9 @@ ollama_selected=0
|
||||
ollama_profile=""
|
||||
|
||||
if [ -n "$CHOICES" ]; then
|
||||
# Whiptail returns a string like "tag1" "tag2" "tag3"
|
||||
# We need to remove quotes and convert to an array
|
||||
# Parse whiptail output safely (without eval)
|
||||
temp_choices=()
|
||||
eval "temp_choices=($CHOICES)"
|
||||
wt_parse_choices "$CHOICES" temp_choices
|
||||
|
||||
for choice in "${temp_choices[@]}"; do
|
||||
if [ "$choice" == "ollama" ]; then
|
||||
@@ -141,11 +138,11 @@ fi
|
||||
# Enforce mutual exclusivity between Dify and Supabase (compact)
|
||||
if printf '%s\n' "${selected_profiles[@]}" | grep -qx "dify" && \
|
||||
printf '%s\n' "${selected_profiles[@]}" | grep -qx "supabase"; then
|
||||
CHOSEN_EXCLUSIVE=$(whiptail --title "Conflict: Dify and Supabase" --default-item "supabase" --radiolist \
|
||||
"Dify and Supabase are mutually exclusive. Choose which one to keep." 15 78 2 \
|
||||
CHOSEN_EXCLUSIVE=$(wt_radiolist "Conflict: Dify and Supabase" \
|
||||
"Dify and Supabase are mutually exclusive. Choose which one to keep." \
|
||||
"supabase" \
|
||||
"dify" "Keep Dify (AI App Platform)" OFF \
|
||||
"supabase" "Keep Supabase (Backend as a Service)" ON \
|
||||
3>&1 1>&2 2>&3)
|
||||
"supabase" "Keep Supabase (Backend as a Service)" ON)
|
||||
[ -z "$CHOSEN_EXCLUSIVE" ] && CHOSEN_EXCLUSIVE="supabase"
|
||||
|
||||
to_remove=$([ "$CHOSEN_EXCLUSIVE" = "dify" ] && echo "supabase" || echo "dify")
|
||||
@@ -187,10 +184,10 @@ if [ $ollama_selected -eq 1 ]; then
|
||||
"gpu-nvidia" "NVIDIA GPU (Requires NVIDIA drivers & CUDA)" "$ollama_hw_on_gpu_nvidia"
|
||||
"gpu-amd" "AMD GPU (Requires ROCm drivers)" "$ollama_hw_on_gpu_amd"
|
||||
)
|
||||
CHOSEN_OLLAMA_PROFILE=$(whiptail --title "Ollama Hardware Profile" --default-item "$default_ollama_hardware" --radiolist \
|
||||
"Choose the hardware profile for Ollama. This will be added to your Docker Compose profiles." 15 78 3 \
|
||||
"${ollama_hardware_options[@]}" \
|
||||
3>&1 1>&2 2>&3)
|
||||
CHOSEN_OLLAMA_PROFILE=$(wt_radiolist "Ollama Hardware Profile" \
|
||||
"Choose the hardware profile for Ollama. This will be added to your Docker Compose profiles." \
|
||||
"$default_ollama_hardware" \
|
||||
"${ollama_hardware_options[@]}")
|
||||
|
||||
ollama_exitstatus=$?
|
||||
if [ $ollama_exitstatus -eq 0 ] && [ -n "$CHOSEN_OLLAMA_PROFILE" ]; then
|
||||
@@ -209,19 +206,19 @@ if [ ${#selected_profiles[@]} -eq 0 ]; then
|
||||
log_info "No optional services selected."
|
||||
COMPOSE_PROFILES_VALUE=""
|
||||
else
|
||||
log_info "You have selected the following service profiles to be deployed:"
|
||||
log_info "Selected service profiles:"
|
||||
# Join the array into a comma-separated string
|
||||
COMPOSE_PROFILES_VALUE=$(IFS=,; echo "${selected_profiles[*]}")
|
||||
for profile in "${selected_profiles[@]}"; do
|
||||
# Check if the current profile is an Ollama hardware profile that was chosen
|
||||
if [[ "$profile" == "cpu" || "$profile" == "gpu-nvidia" || "$profile" == "gpu-amd" ]]; then
|
||||
if [ "$profile" == "$ollama_profile" ]; then # ollama_profile stores the CHOSEN_OLLAMA_PROFILE from this wizard run
|
||||
echo " - Ollama ($profile profile)"
|
||||
else # This handles a (highly unlikely) non-Ollama service named "cpu", "gpu-nvidia", or "gpu-amd"
|
||||
echo " - $profile"
|
||||
if [ "$profile" == "$ollama_profile" ]; then
|
||||
echo -e " ${GREEN}*${NC} Ollama ($profile profile)"
|
||||
else
|
||||
echo -e " ${GREEN}*${NC} $profile"
|
||||
fi
|
||||
else
|
||||
echo " - $profile"
|
||||
echo -e " ${GREEN}*${NC} $profile"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
@@ -27,12 +27,10 @@ init_paths
|
||||
# Ensure .env exists
|
||||
ensure_file_exists "$ENV_FILE"
|
||||
|
||||
log_info "Configuring service options in .env..."
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Prompt for OpenAI API key (optional) using .env value as source of truth
|
||||
# ----------------------------------------------------------------
|
||||
log_subheader "OpenAI API Key"
|
||||
EXISTING_OPENAI_API_KEY="$(read_env_var OPENAI_API_KEY)"
|
||||
OPENAI_API_KEY=""
|
||||
if [[ -z "$EXISTING_OPENAI_API_KEY" ]]; then
|
||||
@@ -50,6 +48,7 @@ fi
|
||||
# ----------------------------------------------------------------
|
||||
# Logic for n8n workflow import (RUN_N8N_IMPORT)
|
||||
# ----------------------------------------------------------------
|
||||
log_subheader "n8n Workflow Import"
|
||||
final_run_n8n_import_decision="false"
|
||||
require_whiptail
|
||||
if wt_yesno "Import n8n Workflows" "Import ~300 ready-made n8n workflows now? This can take ~30 minutes." "no"; then
|
||||
@@ -65,7 +64,7 @@ write_env_var "RUN_N8N_IMPORT" "$final_run_n8n_import_decision"
|
||||
# ----------------------------------------------------------------
|
||||
# Prompt for number of n8n workers
|
||||
# ----------------------------------------------------------------
|
||||
log_info "Configuring n8n worker count..."
|
||||
log_subheader "n8n Worker Configuration"
|
||||
EXISTING_N8N_WORKER_COUNT="$(read_env_var N8N_WORKER_COUNT)"
|
||||
require_whiptail
|
||||
if [[ -n "$EXISTING_N8N_WORKER_COUNT" ]]; then
|
||||
@@ -126,12 +125,12 @@ bash "$SCRIPT_DIR/generate_n8n_workers.sh"
|
||||
# ----------------------------------------------------------------
|
||||
# Cloudflare Tunnel Token (if cloudflare-tunnel profile is active)
|
||||
# ----------------------------------------------------------------
|
||||
# If Cloudflare Tunnel is selected (based on COMPOSE_PROFILES), prompt for the token and write to .env
|
||||
COMPOSE_PROFILES_VALUE="$(read_env_var COMPOSE_PROFILES)"
|
||||
# Set COMPOSE_PROFILES for is_profile_active to work
|
||||
COMPOSE_PROFILES="$COMPOSE_PROFILES_VALUE"
|
||||
|
||||
if is_profile_active "cloudflare-tunnel"; then
|
||||
log_subheader "Cloudflare Tunnel"
|
||||
existing_cf_token="$(read_env_var CLOUDFLARE_TUNNEL_TOKEN)"
|
||||
|
||||
if [ -n "$existing_cf_token" ]; then
|
||||
|
||||
@@ -26,6 +26,7 @@ init_paths
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Check required files
|
||||
log_subheader "Pre-flight Checks"
|
||||
require_file "$ENV_FILE" ".env file not found in project root."
|
||||
require_file "$PROJECT_ROOT/docker-compose.yml" "docker-compose.yml file not found in project root."
|
||||
require_file "$PROJECT_ROOT/Caddyfile" "Caddyfile not found in project root. Reverse proxy might not work."
|
||||
@@ -43,6 +44,7 @@ if [ ! -x "$PROJECT_ROOT/start_services.py" ]; then
|
||||
chmod +x "$PROJECT_ROOT/start_services.py"
|
||||
fi
|
||||
|
||||
log_subheader "Starting Services"
|
||||
log_info "Launching services using start_services.py..."
|
||||
# Execute start_services.py
|
||||
"$PROJECT_ROOT/start_services.py"
|
||||
|
||||
@@ -32,54 +32,73 @@ if [ -f "$SCRIPT_DIR/generate_welcome_page.sh" ]; then
|
||||
bash "$SCRIPT_DIR/generate_welcome_page.sh" || log_warning "Failed to generate welcome page"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "======================================================================="
|
||||
echo " Installation Complete!"
|
||||
echo "======================================================================="
|
||||
echo
|
||||
# Helper function to print a divider line
|
||||
print_line() {
|
||||
echo -e "${DIM}${GREEN}$(printf '%.0s-' {1..70})${NC}"
|
||||
}
|
||||
|
||||
# --- Welcome Page ---
|
||||
echo "================================= Welcome Page =========================="
|
||||
echo
|
||||
echo "All your service credentials are available on the Welcome Page:"
|
||||
echo
|
||||
echo " URL: https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"
|
||||
echo " Username: ${WELCOME_USERNAME:-<not_set>}"
|
||||
echo " Password: ${WELCOME_PASSWORD:-<not_set>}"
|
||||
echo
|
||||
echo "The Welcome Page displays:"
|
||||
echo " - All installed services with their hostnames"
|
||||
echo " - Login credentials (username/password/API keys)"
|
||||
echo " - Internal URLs for service-to-service communication"
|
||||
echo
|
||||
# Helper function to print a credential row
|
||||
print_credential() {
|
||||
local label="$1"
|
||||
local value="$2"
|
||||
printf " ${CYAN}%-12s${NC} ${WHITE}%s${NC}\n" "$label:" "$value"
|
||||
}
|
||||
|
||||
# --- Next Steps ---
|
||||
echo "======================================================================="
|
||||
echo " Next Steps"
|
||||
echo "======================================================================="
|
||||
echo
|
||||
echo "1. Visit your Welcome Page to view all service credentials"
|
||||
echo " https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"
|
||||
echo
|
||||
echo "2. Store the Welcome Page credentials securely"
|
||||
echo
|
||||
echo "3. Configure services as needed:"
|
||||
# Helper function to print section header
|
||||
print_section() {
|
||||
local title="$1"
|
||||
echo ""
|
||||
echo -e "${BOLD}${BRIGHT_GREEN} $title${NC}"
|
||||
echo -e " ${DIM}$(printf '%.0s-' {1..40})${NC}"
|
||||
}
|
||||
|
||||
# Clear screen for clean presentation
|
||||
clear
|
||||
|
||||
# Header
|
||||
log_box "Installation Complete"
|
||||
|
||||
# --- Welcome Page Section ---
|
||||
print_section "Welcome Page"
|
||||
echo ""
|
||||
echo -e " ${WHITE}All your service credentials are available here:${NC}"
|
||||
echo ""
|
||||
print_credential "URL" "https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"
|
||||
print_credential "Username" "${WELCOME_USERNAME:-<not_set>}"
|
||||
print_credential "Password" "${WELCOME_PASSWORD:-<not_set>}"
|
||||
echo ""
|
||||
echo -e " ${DIM}The Welcome Page shows all installed services with their${NC}"
|
||||
echo -e " ${DIM}hostnames, credentials, and internal URLs.${NC}"
|
||||
|
||||
# --- Next Steps Section ---
|
||||
print_section "Next Steps"
|
||||
echo ""
|
||||
echo -e " ${WHITE}1.${NC} Visit your Welcome Page to view all credentials"
|
||||
echo -e " ${CYAN}https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}${NC}"
|
||||
echo ""
|
||||
echo -e " ${WHITE}2.${NC} Store the Welcome Page credentials securely"
|
||||
echo ""
|
||||
echo -e " ${WHITE}3.${NC} Configure services as needed:"
|
||||
if is_profile_active "n8n"; then
|
||||
echo " - n8n: Complete first-run setup with your email"
|
||||
echo -e " ${GREEN}*${NC} ${WHITE}n8n${NC}: Complete first-run setup with your email"
|
||||
fi
|
||||
if is_profile_active "portainer"; then
|
||||
echo " - Portainer: Create admin account on first login"
|
||||
echo -e " ${GREEN}*${NC} ${WHITE}Portainer${NC}: Create admin account on first login"
|
||||
fi
|
||||
if is_profile_active "flowise"; then
|
||||
echo " - Flowise: Register and create your account"
|
||||
echo -e " ${GREEN}*${NC} ${WHITE}Flowise${NC}: Register and create your account"
|
||||
fi
|
||||
if is_profile_active "open-webui"; then
|
||||
echo " - Open WebUI: Register your account"
|
||||
echo -e " ${GREEN}*${NC} ${WHITE}Open WebUI${NC}: Register your account"
|
||||
fi
|
||||
echo
|
||||
echo "4. Run 'make doctor' if you experience any issues"
|
||||
echo
|
||||
echo "======================================================================="
|
||||
echo
|
||||
log_info "Thank you for using n8n-install!"
|
||||
echo
|
||||
echo ""
|
||||
echo -e " ${WHITE}4.${NC} Run ${CYAN}make doctor${NC} if you experience any issues"
|
||||
|
||||
# --- Footer ---
|
||||
echo ""
|
||||
print_line
|
||||
echo ""
|
||||
echo -e " ${BRIGHT_GREEN}Thank you for using n8n-install!${NC}"
|
||||
echo ""
|
||||
print_line
|
||||
echo ""
|
||||
|
||||
@@ -28,15 +28,11 @@ count_error() {
|
||||
ERRORS=$((ERRORS + 1))
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " n8n-install System Diagnostics"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
# Header
|
||||
log_box "n8n-install System Diagnostics"
|
||||
|
||||
# Check if .env file exists
|
||||
echo "Configuration:"
|
||||
echo "--------------"
|
||||
log_subheader "Configuration"
|
||||
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
count_ok ".env file exists"
|
||||
@@ -64,16 +60,12 @@ if [ -f "$ENV_FILE" ]; then
|
||||
fi
|
||||
else
|
||||
count_error ".env file not found at $ENV_FILE"
|
||||
echo ""
|
||||
echo "Run 'make install' to set up the environment."
|
||||
print_info "Run 'make install' to set up the environment."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Check Docker
|
||||
echo "Docker:"
|
||||
echo "-------"
|
||||
log_subheader "Docker"
|
||||
|
||||
if command -v docker &> /dev/null; then
|
||||
count_ok "Docker is installed"
|
||||
@@ -93,11 +85,8 @@ else
|
||||
count_warning "Docker Compose is not available"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Check disk space
|
||||
echo "Disk Space:"
|
||||
echo "-----------"
|
||||
log_subheader "Disk Space"
|
||||
|
||||
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')
|
||||
DISK_AVAIL=$(df -h / | awk 'NR==2 {print $4}')
|
||||
@@ -116,11 +105,8 @@ if [ -n "$DOCKER_DISK" ]; then
|
||||
print_info "Docker using: $DOCKER_DISK"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Check memory
|
||||
echo "Memory:"
|
||||
echo "-------"
|
||||
log_subheader "Memory"
|
||||
|
||||
if command -v free &> /dev/null; then
|
||||
MEM_TOTAL=$(free -h | awk '/^Mem:/ {print $2}')
|
||||
@@ -139,11 +125,8 @@ else
|
||||
print_info "Memory info not available (free command not found)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Check containers
|
||||
echo "Containers:"
|
||||
echo "-----------"
|
||||
log_subheader "Containers"
|
||||
|
||||
RUNNING=$(docker ps -q 2>/dev/null | wc -l)
|
||||
TOTAL=$(docker ps -aq 2>/dev/null | wc -l)
|
||||
@@ -181,11 +164,8 @@ else
|
||||
count_ok "No unhealthy containers"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Check DNS resolution
|
||||
echo "DNS Resolution:"
|
||||
echo "---------------"
|
||||
log_subheader "DNS Resolution"
|
||||
|
||||
check_dns() {
|
||||
local hostname="$1"
|
||||
@@ -212,11 +192,8 @@ else
|
||||
print_info "Skipping DNS checks (no domain configured)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Check SSL (Caddy)
|
||||
echo "SSL/Caddy:"
|
||||
echo "----------"
|
||||
log_subheader "SSL/Caddy"
|
||||
|
||||
if docker ps --format '{{.Names}}' 2>/dev/null | grep -q "caddy"; then
|
||||
count_ok "Caddy container is running"
|
||||
@@ -231,11 +208,8 @@ else
|
||||
count_warning "Caddy container is not running"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Check key services
|
||||
echo "Key Services:"
|
||||
echo "-------------"
|
||||
log_subheader "Key Services"
|
||||
|
||||
check_service() {
|
||||
local container="$1"
|
||||
@@ -263,26 +237,24 @@ if is_profile_active "monitoring"; then
|
||||
check_service "prometheus" "9090"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo "========================================"
|
||||
echo " Summary"
|
||||
echo "========================================"
|
||||
log_box "Summary"
|
||||
echo ""
|
||||
|
||||
echo -e " ${GREEN}OK:${NC} $OK"
|
||||
echo -e " ${YELLOW}Warnings:${NC} $WARNINGS"
|
||||
echo -e " ${RED}Errors:${NC} $ERRORS"
|
||||
echo -e " ${GREEN}OK:${NC} ${BOLD}$OK${NC}"
|
||||
echo -e " ${YELLOW}Warnings:${NC} ${BOLD}$WARNINGS${NC}"
|
||||
echo -e " ${RED}Errors:${NC} ${BOLD}$ERRORS${NC}"
|
||||
echo ""
|
||||
|
||||
if [ $ERRORS -gt 0 ]; then
|
||||
echo -e "${RED}Some issues were found. Please review the errors above.${NC}"
|
||||
echo -e " ${BG_RED}${WHITE} ISSUES FOUND ${NC}"
|
||||
echo -e " ${RED}Please review the errors above and take action.${NC}"
|
||||
exit 1
|
||||
elif [ $WARNINGS -gt 0 ]; then
|
||||
echo -e "${YELLOW}System is mostly healthy with some warnings.${NC}"
|
||||
echo -e " ${BG_YELLOW}${WHITE} MOSTLY HEALTHY ${NC}"
|
||||
echo -e " ${YELLOW}System is functional with some warnings.${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${GREEN}System is healthy!${NC}"
|
||||
echo -e " ${BG_GREEN}${WHITE} HEALTHY ${NC}"
|
||||
echo -e " ${GREEN}All checks passed successfully!${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -97,42 +97,42 @@ fi
|
||||
|
||||
# Run installation steps sequentially using their full paths
|
||||
|
||||
log_header "STEP 1: System Preparation"
|
||||
show_step 1 7 "System Preparation"
|
||||
bash "$SCRIPT_DIR/01_system_preparation.sh" || { log_error "System Preparation failed"; exit 1; }
|
||||
log_success "System preparation complete!"
|
||||
|
||||
log_header "STEP 2: Installing Docker"
|
||||
show_step 2 7 "Installing Docker"
|
||||
bash "$SCRIPT_DIR/02_install_docker.sh" || { log_error "Docker Installation failed"; exit 1; }
|
||||
log_success "Docker installation complete!"
|
||||
|
||||
log_header "STEP 3: Generating Secrets and Configuration"
|
||||
show_step 3 7 "Generating Secrets and Configuration"
|
||||
bash "$SCRIPT_DIR/03_generate_secrets.sh" || { log_error "Secret/Config Generation failed"; exit 1; }
|
||||
log_success "Secret/Config Generation complete!"
|
||||
|
||||
log_header "STEP 4: Running Service Selection Wizard"
|
||||
show_step 4 7 "Running Service Selection Wizard"
|
||||
bash "$SCRIPT_DIR/04_wizard.sh" || { log_error "Service Selection Wizard failed"; exit 1; }
|
||||
log_success "Service Selection Wizard complete!"
|
||||
|
||||
log_header "STEP 5: Configure Services"
|
||||
show_step 5 7 "Configure Services"
|
||||
bash "$SCRIPT_DIR/05_configure_services.sh" || { log_error "Configure Services failed"; exit 1; }
|
||||
log_success "Configure Services complete!"
|
||||
|
||||
log_header "STEP 6: Running Services"
|
||||
show_step 6 7 "Running Services"
|
||||
bash "$SCRIPT_DIR/06_run_services.sh" || { log_error "Running Services failed"; exit 1; }
|
||||
log_success "Running Services complete!"
|
||||
|
||||
log_header "STEP 7: Generating Final Report"
|
||||
show_step 7 7 "Generating Final Report"
|
||||
# --- Installation Summary ---
|
||||
log_info "Installation Summary. The following steps were performed by the scripts:"
|
||||
log_success "- System updated and basic utilities installed"
|
||||
log_success "- Firewall (UFW) configured and enabled"
|
||||
log_success "- Fail2Ban activated for brute-force protection"
|
||||
log_success "- Automatic security updates enabled"
|
||||
log_success "- Docker and Docker Compose installed"
|
||||
log_success "- '.env' generated with secure passwords and secrets"
|
||||
log_success "- Services launched via Docker Compose"
|
||||
log_info "Installation Summary:"
|
||||
echo -e " ${GREEN}*${NC} System updated and basic utilities installed"
|
||||
echo -e " ${GREEN}*${NC} Firewall (UFW) configured and enabled"
|
||||
echo -e " ${GREEN}*${NC} Fail2Ban activated for brute-force protection"
|
||||
echo -e " ${GREEN}*${NC} Automatic security updates enabled"
|
||||
echo -e " ${GREEN}*${NC} Docker and Docker Compose installed"
|
||||
echo -e " ${GREEN}*${NC} '.env' generated with secure passwords and secrets"
|
||||
echo -e " ${GREEN}*${NC} Services launched via Docker Compose"
|
||||
|
||||
bash "$SCRIPT_DIR/07_final_report.sh" || { log_error "Final Report Generation failed"; exit 1; }
|
||||
log_success "Final Report Generation complete!"
|
||||
log_success "Installation complete!"
|
||||
|
||||
exit 0
|
||||
@@ -12,12 +12,9 @@ init_paths
|
||||
# Load environment variables
|
||||
load_env || exit 1
|
||||
|
||||
log_box "Update Preview (Dry Run)"
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " Update Preview (Dry Run)"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "Checking for available updates..."
|
||||
echo -e " ${CYAN}Checking for available updates...${NC}"
|
||||
echo ""
|
||||
|
||||
# Function to get local image digest
|
||||
@@ -74,108 +71,84 @@ log_info "Scanning images from docker-compose.yml..."
|
||||
echo ""
|
||||
|
||||
# Core services (always checked)
|
||||
echo "Core Services:"
|
||||
echo "--------------"
|
||||
log_subheader "Core Services"
|
||||
check_image_update "postgres" "postgres:${POSTGRES_VERSION:-17}-alpine"
|
||||
check_image_update "redis" "valkey/valkey:8-alpine"
|
||||
check_image_update "caddy" "caddy:2-alpine"
|
||||
echo ""
|
||||
|
||||
# Check n8n if profile is active
|
||||
if is_profile_active "n8n"; then
|
||||
echo "n8n Services:"
|
||||
echo "-------------"
|
||||
log_subheader "n8n Services"
|
||||
check_image_update "n8n" "docker.n8n.io/n8nio/n8n:${N8N_VERSION:-latest}"
|
||||
check_image_update "n8n-runner" "n8nio/runners:${N8N_VERSION:-latest}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Check monitoring if profile is active
|
||||
if is_profile_active "monitoring"; then
|
||||
echo "Monitoring Services:"
|
||||
echo "--------------------"
|
||||
log_subheader "Monitoring Services"
|
||||
check_image_update "grafana" "grafana/grafana:latest"
|
||||
check_image_update "prometheus" "prom/prometheus:latest"
|
||||
check_image_update "node-exporter" "prom/node-exporter:latest"
|
||||
check_image_update "cadvisor" "gcr.io/cadvisor/cadvisor:latest"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Check other common services
|
||||
if is_profile_active "flowise"; then
|
||||
echo "Flowise:"
|
||||
echo "--------"
|
||||
log_subheader "Flowise"
|
||||
check_image_update "flowise" "flowiseai/flowise:latest"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if is_profile_active "open-webui"; then
|
||||
echo "Open WebUI:"
|
||||
echo "-----------"
|
||||
log_subheader "Open WebUI"
|
||||
check_image_update "open-webui" "ghcr.io/open-webui/open-webui:main"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if is_profile_active "portainer"; then
|
||||
echo "Portainer:"
|
||||
echo "----------"
|
||||
log_subheader "Portainer"
|
||||
check_image_update "portainer" "portainer/portainer-ce:latest"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if is_profile_active "langfuse"; then
|
||||
echo "Langfuse:"
|
||||
echo "---------"
|
||||
log_subheader "Langfuse"
|
||||
check_image_update "langfuse-web" "langfuse/langfuse:latest"
|
||||
check_image_update "langfuse-worker" "langfuse/langfuse-worker:latest"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if is_profile_active "cpu" || is_profile_active "gpu-nvidia" || is_profile_active "gpu-amd"; then
|
||||
echo "Ollama:"
|
||||
echo "-------"
|
||||
log_subheader "Ollama"
|
||||
check_image_update "ollama" "ollama/ollama:latest"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if is_profile_active "qdrant"; then
|
||||
echo "Qdrant:"
|
||||
echo "-------"
|
||||
log_subheader "Qdrant"
|
||||
check_image_update "qdrant" "qdrant/qdrant:latest"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if is_profile_active "searxng"; then
|
||||
echo "SearXNG:"
|
||||
echo "--------"
|
||||
log_subheader "SearXNG"
|
||||
check_image_update "searxng" "searxng/searxng:latest"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if is_profile_active "postgresus"; then
|
||||
echo "Postgresus:"
|
||||
echo "-----------"
|
||||
log_subheader "Postgresus"
|
||||
check_image_update "postgresus" "ghcr.io/postgresus/postgresus:latest"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo "========================================"
|
||||
echo " Summary"
|
||||
echo "========================================"
|
||||
log_divider
|
||||
echo ""
|
||||
|
||||
if [ $UPDATES_AVAILABLE -gt 0 ]; then
|
||||
echo -e "${GREEN}$UPDATES_AVAILABLE update(s) available.${NC}"
|
||||
echo -e " ${BRIGHT_GREEN}$UPDATES_AVAILABLE update(s) available!${NC}"
|
||||
echo ""
|
||||
echo "To apply updates, run:"
|
||||
echo " make update"
|
||||
echo -e " ${WHITE}To apply updates, run:${NC}"
|
||||
echo -e " ${CYAN}make update${NC}"
|
||||
echo ""
|
||||
echo "Or manually:"
|
||||
echo " docker compose -p localai pull"
|
||||
echo " docker compose -p localai up -d"
|
||||
echo -e " ${DIM}Or manually:${NC}"
|
||||
echo -e " ${DIM}docker compose -p localai pull${NC}"
|
||||
echo -e " ${DIM}docker compose -p localai up -d${NC}"
|
||||
else
|
||||
echo -e "${GREEN}All images are up to date!${NC}"
|
||||
echo -e " ${BRIGHT_GREEN}All images are up to date!${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
275
scripts/utils.sh
275
scripts/utils.sh
@@ -21,6 +21,39 @@
|
||||
#=============================================================================
|
||||
DOMAIN_PLACEHOLDER="yourdomain.com"
|
||||
|
||||
#=============================================================================
|
||||
# WHIPTAIL THEME (NEWT_COLORS)
|
||||
#=============================================================================
|
||||
# Dark theme with green/cyan accents on black background
|
||||
# Format: element=foreground,background
|
||||
# Colors: black, red, green, yellow, blue, magenta, cyan, white
|
||||
# Prefix with "bright" for bright variants (e.g., brightgreen)
|
||||
export NEWT_COLORS='
|
||||
root=white,black
|
||||
border=brightgreen,black
|
||||
window=white,black
|
||||
shadow=black,black
|
||||
title=brightgreen,black
|
||||
button=black,brightgreen
|
||||
actbutton=black,brightcyan
|
||||
compactbutton=white,black
|
||||
checkbox=brightgreen,black
|
||||
actcheckbox=black,brightgreen
|
||||
entry=brightcyan,black
|
||||
disentry=gray,black
|
||||
label=white,black
|
||||
listbox=white,black
|
||||
actlistbox=black,brightgreen
|
||||
sellistbox=brightcyan,black
|
||||
actsellistbox=black,brightcyan
|
||||
textbox=white,black
|
||||
acttextbox=brightgreen,black
|
||||
emptyscale=black,black
|
||||
fullscale=brightgreen,black
|
||||
helpline=brightcyan,black
|
||||
roottext=brightgreen,black
|
||||
'
|
||||
|
||||
#=============================================================================
|
||||
# PATH INITIALIZATION
|
||||
#=============================================================================
|
||||
@@ -65,21 +98,77 @@ log_error() {
|
||||
}
|
||||
|
||||
# Display a header for major sections
|
||||
# Usage: log_header "Section Title"
|
||||
log_header() {
|
||||
local message="$1"
|
||||
local width=60
|
||||
local padding=$(( (width - ${#message} - 2) / 2 ))
|
||||
local pad_left=$(printf '%*s' "$padding" '' | tr ' ' '=')
|
||||
local pad_right=$(printf '%*s' "$((width - ${#message} - 2 - padding))" '' | tr ' ' '=')
|
||||
|
||||
echo ""
|
||||
echo ""
|
||||
echo "========== $message =========="
|
||||
echo -e "${BRIGHT_GREEN}${pad_left}${NC} ${BOLD}${WHITE}${message}${NC} ${BRIGHT_GREEN}${pad_right}${NC}"
|
||||
}
|
||||
|
||||
# Display a sub-header for sections
|
||||
# Usage: log_subheader "Sub Section"
|
||||
log_subheader() {
|
||||
local message="$1"
|
||||
echo ""
|
||||
echo -e "${CYAN}--- ${message} ---${NC}"
|
||||
}
|
||||
|
||||
# Display a divider line
|
||||
# Usage: log_divider
|
||||
log_divider() {
|
||||
echo ""
|
||||
echo -e "${DIM}${GREEN}$(printf '%.0s-' {1..60})${NC}"
|
||||
}
|
||||
|
||||
# Display text in a box (for important messages)
|
||||
# Usage: log_box "Important message"
|
||||
log_box() {
|
||||
local message="$1"
|
||||
local len=${#message}
|
||||
local border=$(printf '%*s' "$((len + 4))" '' | tr ' ' '=')
|
||||
|
||||
echo ""
|
||||
echo -e "${BRIGHT_GREEN}+${border}+${NC}"
|
||||
echo -e "${BRIGHT_GREEN}|${NC} ${BOLD}${WHITE}${message}${NC} ${BRIGHT_GREEN}|${NC}"
|
||||
echo -e "${BRIGHT_GREEN}+${border}+${NC}"
|
||||
}
|
||||
|
||||
#=============================================================================
|
||||
# COLOR OUTPUT (for diagnostics and previews)
|
||||
#=============================================================================
|
||||
# Basic colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
CYAN='\033[0;36m'
|
||||
MAGENTA='\033[0;35m'
|
||||
WHITE='\033[1;37m'
|
||||
GRAY='\033[0;90m'
|
||||
NC='\033[0m' # No Color / Reset
|
||||
|
||||
# Text styles
|
||||
BOLD='\033[1m'
|
||||
DIM='\033[2m'
|
||||
RESET='\033[0m'
|
||||
|
||||
# Bright colors
|
||||
BRIGHT_RED='\033[1;31m'
|
||||
BRIGHT_GREEN='\033[1;32m'
|
||||
BRIGHT_YELLOW='\033[1;33m'
|
||||
BRIGHT_BLUE='\033[1;34m'
|
||||
BRIGHT_CYAN='\033[1;36m'
|
||||
|
||||
# Background colors (for badges/labels)
|
||||
BG_RED='\033[41m'
|
||||
BG_GREEN='\033[42m'
|
||||
BG_YELLOW='\033[43m'
|
||||
|
||||
print_ok() {
|
||||
echo ""
|
||||
@@ -101,6 +190,85 @@ print_info() {
|
||||
echo -e " ${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
#=============================================================================
|
||||
# PROGRESS INDICATORS
|
||||
#=============================================================================
|
||||
|
||||
# Spinner animation frames
|
||||
SPINNER_FRAMES=('|' '/' '-' '\')
|
||||
SPINNER_PID=""
|
||||
|
||||
# Start spinner with message
|
||||
# Usage: start_spinner "Loading..."
|
||||
start_spinner() {
|
||||
local message="$1"
|
||||
local i=0
|
||||
|
||||
# Don't start if not in terminal or already running
|
||||
[[ ! -t 1 ]] && return
|
||||
[[ -n "$SPINNER_PID" ]] && return
|
||||
|
||||
(
|
||||
while true; do
|
||||
printf "\r ${GREEN}${SPINNER_FRAMES[$i]}${NC} ${message} "
|
||||
i=$(( (i + 1) % 4 ))
|
||||
sleep 0.1
|
||||
done
|
||||
) &
|
||||
SPINNER_PID=$!
|
||||
disown
|
||||
}
|
||||
|
||||
# Stop spinner and clear line
|
||||
# Usage: stop_spinner
|
||||
stop_spinner() {
|
||||
[[ -z "$SPINNER_PID" ]] && return
|
||||
|
||||
kill "$SPINNER_PID" 2>/dev/null
|
||||
wait "$SPINNER_PID" 2>/dev/null
|
||||
SPINNER_PID=""
|
||||
|
||||
# Clear the spinner line
|
||||
printf "\r%*s\r" 80 ""
|
||||
}
|
||||
|
||||
# Show step progress (e.g., Step 3/7)
|
||||
# Usage: show_step 3 7 "Installing Docker"
|
||||
show_step() {
|
||||
local current=$1
|
||||
local total=$2
|
||||
local description="$3"
|
||||
|
||||
echo ""
|
||||
echo -e "${BRIGHT_GREEN}[${current}/${total}]${NC} ${BOLD}${description}${NC}"
|
||||
echo -e "${DIM}$(printf '%.0s.' {1..50})${NC}"
|
||||
}
|
||||
|
||||
# Show a simple progress bar
|
||||
# Usage: show_progress 50 100 "Downloading"
|
||||
show_progress() {
|
||||
local current=$1
|
||||
local total=$2
|
||||
local label="${3:-Progress}"
|
||||
local width=40
|
||||
local percent=$(( current * 100 / total ))
|
||||
local filled=$(( current * width / total ))
|
||||
local empty=$(( width - filled ))
|
||||
|
||||
local bar_filled=$(printf '%*s' "$filled" '' | tr ' ' '#')
|
||||
local bar_empty=$(printf '%*s' "$empty" '' | tr ' ' '-')
|
||||
|
||||
printf "\r ${label}: ${GREEN}[${bar_filled}${GRAY}${bar_empty}${GREEN}]${NC} ${WHITE}%3d%%${NC}" "$percent"
|
||||
}
|
||||
|
||||
# Complete progress bar with message
|
||||
# Usage: complete_progress "Download complete"
|
||||
complete_progress() {
|
||||
local message="${1:-Done}"
|
||||
printf "\r%*s\r" 80 ""
|
||||
echo -e " ${GREEN}[OK]${NC} ${message}"
|
||||
}
|
||||
|
||||
#=============================================================================
|
||||
# ENVIRONMENT MANAGEMENT
|
||||
#=============================================================================
|
||||
@@ -308,57 +476,130 @@ require_whiptail() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Input box
|
||||
# Get adaptive terminal size for whiptail dialogs
|
||||
# Usage: eval "$(wt_get_size)"
|
||||
# Sets: WT_HEIGHT, WT_WIDTH, WT_LIST_HEIGHT
|
||||
wt_get_size() {
|
||||
local term_lines term_cols
|
||||
term_lines=$(tput lines 2>/dev/null || echo 24)
|
||||
term_cols=$(tput cols 2>/dev/null || echo 80)
|
||||
|
||||
# Calculate dimensions with margins
|
||||
local height=$((term_lines - 4))
|
||||
local width=$((term_cols - 4))
|
||||
|
||||
# Apply min/max constraints
|
||||
[[ $height -lt 10 ]] && height=10
|
||||
[[ $height -gt 40 ]] && height=40
|
||||
[[ $width -lt 60 ]] && width=60
|
||||
[[ $width -gt 120 ]] && width=120
|
||||
|
||||
# List height for checklists/menus (leave room for title, prompt, buttons)
|
||||
local list_height=$((height - 8))
|
||||
[[ $list_height -lt 5 ]] && list_height=5
|
||||
|
||||
echo "WT_HEIGHT=$height WT_WIDTH=$width WT_LIST_HEIGHT=$list_height"
|
||||
}
|
||||
|
||||
# Input box with adaptive sizing
|
||||
# Usage: result=$(wt_input "Title" "Prompt" "default")
|
||||
# Returns 0 on OK, 1 on Cancel
|
||||
wt_input() {
|
||||
local title="$1"
|
||||
local prompt="$2"
|
||||
local default_value="$3"
|
||||
eval "$(wt_get_size)"
|
||||
local result
|
||||
result=$(whiptail --title "$title" --inputbox "$prompt" 15 80 "$default_value" 3>&1 1>&2 2>&3)
|
||||
result=$(whiptail --title "$title" --inputbox "$prompt" "$WT_HEIGHT" "$WT_WIDTH" "$default_value" 3>&1 1>&2 2>&3)
|
||||
local status=$?
|
||||
if [ $status -ne 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
[[ $status -ne 0 ]] && return 1
|
||||
echo "$result"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Password box
|
||||
# Password box with adaptive sizing
|
||||
# Usage: result=$(wt_password "Title" "Prompt")
|
||||
# Returns 0 on OK, 1 on Cancel
|
||||
wt_password() {
|
||||
local title="$1"
|
||||
local prompt="$2"
|
||||
eval "$(wt_get_size)"
|
||||
local result
|
||||
result=$(whiptail --title "$title" --passwordbox "$prompt" 15 80 3>&1 1>&2 2>&3)
|
||||
result=$(whiptail --title "$title" --passwordbox "$prompt" "$WT_HEIGHT" "$WT_WIDTH" 3>&1 1>&2 2>&3)
|
||||
local status=$?
|
||||
if [ $status -ne 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
[[ $status -ne 0 ]] && return 1
|
||||
echo "$result"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Yes/No box
|
||||
# Yes/No box with adaptive sizing
|
||||
# Usage: wt_yesno "Title" "Prompt" "default" (default: yes|no)
|
||||
# Returns 0 for Yes, 1 for No/Cancel
|
||||
wt_yesno() {
|
||||
local title="$1"
|
||||
local prompt="$2"
|
||||
local default_choice="$3"
|
||||
eval "$(wt_get_size)"
|
||||
local height=$((WT_HEIGHT < 12 ? WT_HEIGHT : 12))
|
||||
if [ "$default_choice" = "yes" ]; then
|
||||
whiptail --title "$title" --yesno "$prompt" 10 80
|
||||
whiptail --title "$title" --yesno "$prompt" "$height" "$WT_WIDTH"
|
||||
else
|
||||
whiptail --title "$title" --defaultno --yesno "$prompt" 10 80
|
||||
whiptail --title "$title" --defaultno --yesno "$prompt" "$height" "$WT_WIDTH"
|
||||
fi
|
||||
}
|
||||
|
||||
# Message box
|
||||
# Message box with adaptive sizing
|
||||
# Usage: wt_msg "Title" "Message"
|
||||
wt_msg() {
|
||||
local title="$1"
|
||||
local message="$2"
|
||||
whiptail --title "$title" --msgbox "$message" 10 80
|
||||
eval "$(wt_get_size)"
|
||||
local height=$((WT_HEIGHT < 12 ? WT_HEIGHT : 12))
|
||||
whiptail --title "$title" --msgbox "$message" "$height" "$WT_WIDTH"
|
||||
}
|
||||
|
||||
# Checklist (multiple selection) with adaptive sizing
|
||||
# Usage: result=$(wt_checklist "Title" "Prompt" "tag1" "desc1" "ON" "tag2" "desc2" "OFF" ...)
|
||||
# Returns: space-separated quoted tags, e.g., "tag1" "tag2"
|
||||
wt_checklist() {
|
||||
local title="$1"
|
||||
local prompt="$2"
|
||||
shift 2
|
||||
eval "$(wt_get_size)"
|
||||
whiptail --title "$title" --checklist "$prompt" "$WT_HEIGHT" "$WT_WIDTH" "$WT_LIST_HEIGHT" "$@" 3>&1 1>&2 2>&3
|
||||
}
|
||||
|
||||
# Radiolist (single selection) with adaptive sizing
|
||||
# Usage: result=$(wt_radiolist "Title" "Prompt" "default_item" "tag1" "desc1" "ON" ...)
|
||||
# Returns: selected tag
|
||||
wt_radiolist() {
|
||||
local title="$1"
|
||||
local prompt="$2"
|
||||
local default_item="$3"
|
||||
shift 3
|
||||
eval "$(wt_get_size)"
|
||||
whiptail --title "$title" --default-item "$default_item" --radiolist "$prompt" "$WT_HEIGHT" "$WT_WIDTH" "$WT_LIST_HEIGHT" "$@" 3>&1 1>&2 2>&3
|
||||
}
|
||||
|
||||
# Menu (item selection) with adaptive sizing
|
||||
# Usage: result=$(wt_menu "Title" "Prompt" "tag1" "desc1" "tag2" "desc2" ...)
|
||||
# Returns: selected tag
|
||||
wt_menu() {
|
||||
local title="$1"
|
||||
local prompt="$2"
|
||||
shift 2
|
||||
eval "$(wt_get_size)"
|
||||
whiptail --title "$title" --menu "$prompt" "$WT_HEIGHT" "$WT_WIDTH" "$WT_LIST_HEIGHT" "$@" 3>&1 1>&2 2>&3
|
||||
}
|
||||
|
||||
# Safe parser for whiptail checklist results (replaces eval)
|
||||
# Usage: wt_parse_choices "$CHOICES" result_array
|
||||
# Parses quoted output like: "tag1" "tag2" "tag3"
|
||||
wt_parse_choices() {
|
||||
local choices="$1"
|
||||
local -n arr="$2"
|
||||
arr=()
|
||||
# Remove quotes and split by spaces
|
||||
local cleaned="${choices//\"/}"
|
||||
read -ra arr <<< "$cleaned"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user