diff --git a/NEW-SCRIPT-STRUCTURE.md b/NEW-SCRIPT-STRUCTURE.md new file mode 100644 index 0000000..c126012 --- /dev/null +++ b/NEW-SCRIPT-STRUCTURE.md @@ -0,0 +1,244 @@ +# Ubuntu Post-Install Script - New Structure + +## Current Problems + +1. ❌ Services prompt individually even when not selected in whiptail +2. ❌ Too many things installed BEFORE whiptail menu (Samba, VPNs, fail2ban) +3. ❌ No clear explanation to user about script phases +4. ❌ No uninstall option +5. ❌ Confusing for re-running on existing servers + +## New Structure + +### PHASE 1: Essential System Setup (Before Whiptail) +**Purpose:** Install only what's REQUIRED for everything else to work + +**What stays BEFORE whiptail:** +- ✅ System updates (apt update/upgrade) +- ✅ Essential packages (openssh-server, rsync, curl, wget, git, vim, htop, ncdu) +- ✅ SSH key generation (optional) +- ✅ SSH key import from GitHub/Launchpad (optional) +- ✅ Docker & Docker Compose installation +- ✅ Hard drive mounting/formatting + +**What moves TO whiptail menu:** +- 🔄 Samba file sharing +- 🔄 fail2ban for SSH +- 🔄 VPN services (Tailscale, WireGuard, NetBird) +- 🔄 Remote desktop (RustDesk, TeamViewer) +- 🔄 MeshCentral agent + +### PHASE 2: Service Selection (Whiptail Menu) +**Purpose:** Let user choose ALL optional services + +**New whiptail menu structure:** + +``` +┌─────────────────── Select Services ────────────────────┐ +│ ☐ INSTALL ☑ UNINSTALL │ +│ │ +│ === NETWORK & SECURITY === │ +│ [ ] SAMBA File sharing (SMB/CIFS) │ +│ [ ] FAIL2BAN_SSH Protect SSH from brute-force │ +│ [ ] TAILSCALE Easy VPN mesh network │ +│ [ ] NETBIRD Self-hosted VPN │ +│ [ ] WIREGUARD Manual VPN configuration │ +│ │ +│ === REMOTE ACCESS === │ +│ [ ] RUSTDESK Remote desktop (OSS) │ +│ [ ] TEAMVIEWER Remote desktop (commercial) │ +│ [ ] MESHCENTRAL Remote management agent │ +│ │ +│ === DOCKER SERVICES === │ +│ [ ] ACTUALBUDGET Personal finance │ +│ [ ] KEYCLOAK Identity management │ +│ [ ] CADDY Reverse proxy │ +│ [ ] FAIL2BAN_CADDY Protect Caddy services │ +│ [ ] JELLYFIN Media server │ +│ [ ] IMMICH Photo backup │ +│ [ ] AUDIOBOOKSHELF Audiobook server │ +│ [ ] MEALIE Recipe manager │ +│ [ ] UPTIMEKUMA Service monitoring │ +│ [ ] PORTAINER Docker management UI │ +│ [ ] WATCHTOWER Auto-update containers │ +│ ... (all other services) │ +│ │ +│ === MESHCENTRAL SERVER === │ +│ [ ] MESHCENTRAL_SRV Self-hosted remote mgmt server │ +│ │ +│ │ +└─────────────────────────────────────────────────────────┘ +``` + +### PHASE 3: Installation +**Purpose:** Install/uninstall selected services in correct order + +**Dependency-aware installation order:** +1. Install Caddy first (if selected) +2. Install services that depend on Caddy (Keycloak, etc.) +3. Install fail2ban for Caddy (if selected + Caddy installed) +4. Install independent services in parallel where possible + +### Re-Running The Script + +**Behavior on already-configured server:** + +**Phase 1 (Essential):** +- Detects existing installations +- Shows: "Docker is already installed: (version)" +- Prompts: "Reinstall Docker? (y/n): **n**" (defaults to NO) +- Skips if "n" + +**Phase 2 (Services):** +- Whiptail menu shows ALL services +- Already-installed services could be marked with (installed) +- User can: + - Select new services to add + - Select installed services + click "Uninstall Selected" + - Skip menu to make no changes + +## Implementation Changes + +### 1. Add Intro Text + +```bash +# At beginning of script after argument parsing +if [ "$UNATTENDED" != true ]; then + cat << 'EOF' +╔════════════════════════════════════════════════════════════════╗ +║ Ubuntu Post-Install Setup Script ║ +╚════════════════════════════════════════════════════════════════╝ + +This script installs and configures your Ubuntu server in TWO phases: + +PHASE 1: ESSENTIAL SETUP (Required) + • System updates & core packages + • SSH configuration + • Docker & Docker Compose + • Hard drive mounting + +PHASE 2: SERVICE SELECTION (Optional) + • Interactive menu to select services + • Install OR uninstall services + • Safe to re-run on existing servers + +Press ENTER to begin Phase 1... +EOF + read -p "" +fi +``` + +### 2. Reorganize Script Sections + +**NEW ORDER:** +1. Intro text +2. Essential system setup (Phase 1) +3. Whiptail menu with ALL services (Phase 2) +4. Service installations based on selections + +**REMOVED FROM PRE-WHIPTAIL:** +- Lines 1709-1800: Samba installation → Move to whiptail +- Lines 1596-1625: fail2ban (SSH) → Move to whiptail +- Lines 1841-1888: NetBird → Move to whiptail +- Lines 1889-1945: WireGuard → Move to whiptail +- Lines 1947-1998: Tailscale → Move to whiptail +- Lines 2000-2044: RustDesk → Move to whiptail +- Lines 2046-2095: TeamViewer → Move to whiptail +- Lines 2097-2153: MeshCentral Agent → Move to whiptail + +### 3. Update Whiptail Menu + +**Add these to the checklist:** +```bash +"SAMBA" "File sharing (Windows, Mac, Linux)" OFF \ +"FAIL2BAN_SSH" "Protect SSH from brute-force attacks" OFF \ +"TAILSCALE" "Easy VPN mesh network" OFF \ +"NETBIRD" "Self-hosted VPN alternative" OFF \ +"WIREGUARD" "Manual VPN configuration" OFF \ +"RUSTDESK" "Open-source remote desktop" OFF \ +"TEAMVIEWER" "Commercial remote desktop" OFF \ +"MESHCENTRAL_AGENT" "Remote management agent" OFF \ +``` + +### 4. Add Uninstall Functionality + +**New buttons in whiptail:** +```bash +--extra-button --extra-label "Uninstall" \ +--ok-button "Install" --cancel-button "Skip" +``` + +**Check return code:** +- 0 = Install selected +- 1 = Skip/Cancel +- 3 = Uninstall selected + +**Uninstall logic:** +```bash +if [ $WHIPTAIL_RETURN -eq 3 ]; then + # Uninstall mode + for service in $SELECTED_SERVICES; do + uninstall_service "$service" + done +fi +``` + +### 5. Fix Duplicate Prompts + +**Current issue:** +```bash +if [ -z "$INSTALL_AUDIOBOOKSHELF" ]; then + prompt_yn "Install Audiobookshelf? (y/n):" "n" INSTALL_AUDIOBOOKSHELF +fi +``` + +**Problem:** This runs even if user didn't select it in whiptail! + +**Fix:** Only show prompt if whiptail wasn't used OR variable not set by whiptail + +```bash +# After whiptail, set a flag +WHIPTAIL_USED=true + +# In individual service sections +if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_AUDIOBOOKSHELF" ]; then + prompt_yn "Install Audiobookshelf? (y/n):" "n" INSTALL_AUDIOBOOKSHELF +fi +``` + +**Or simpler:** Just remove all individual prompts! If whiptail is available, use it. If not, show ALL services as prompts. + +## Benefits + +1. ✅ Clear two-phase structure +2. ✅ All optional services in ONE menu +3. ✅ No duplicate prompts +4. ✅ Uninstall functionality +5. ✅ Safe to re-run +6. ✅ User understands what's happening when +7. ✅ Faster for users who know what they want + +## Migration Path + +**For existing users:** +1. Script still works the same way +2. New intro text explains structure +3. Existing installations detected +4. Can use uninstall to remove unwanted services + +## Testing Scenarios + +1. **Fresh install:** All prompts flow correctly +2. **Re-run:** Detects existing, allows adding new services +3. **Uninstall:** Removes selected services cleanly +4. **Whiptail unavailable:** Falls back to individual prompts +5. **Cancel menu:** Skips all service installations + +--- + +**Implementation Priority:** +1. ✅ Fix duplicate prompts (CRITICAL - doing now) +2. ⏳ Add intro text (HIGH) +3. ⏳ Move services to whiptail (HIGH) +4. ⏳ Add uninstall functionality (MEDIUM) +5. ⏳ Improve re-run detection (LOW - already works) diff --git a/SCRIPT-FLOW-INTRO.txt b/SCRIPT-FLOW-INTRO.txt new file mode 100644 index 0000000..e4aef69 --- /dev/null +++ b/SCRIPT-FLOW-INTRO.txt @@ -0,0 +1,59 @@ +#!/bin/bash + +# SCRIPT FLOW EXPLANATION FOR USERS +cat << 'EOF' +╔════════════════════════════════════════════════════════════════╗ +║ Ubuntu Post-Install Setup Script ║ +╚════════════════════════════════════════════════════════════════╝ + +This script is divided into TWO main phases: + +┌────────────────────────────────────────────────────────────────┐ +│ PHASE 1: ESSENTIAL SYSTEM SETUP (Required) │ +├────────────────────────────────────────────────────────────────┤ +│ These are installed/configured FIRST: │ +│ ✓ System updates and essential packages │ +│ ✓ OpenSSH server (remote access) │ +│ ✓ rsync, curl, wget (core utilities) │ +│ ✓ SSH key configuration │ +│ ✓ Docker & Docker Compose (container platform) │ +│ ✓ Hard drive mounting (storage setup) │ +│ │ +│ These are REQUIRED for everything else to work. │ +└────────────────────────────────────────────────────────────────┘ + +┌────────────────────────────────────────────────────────────────┐ +│ PHASE 2: SERVICE SELECTION MENU (Optional) │ +├────────────────────────────────────────────────────────────────┤ +│ After Phase 1, you'll see a CHECKBOX MENU where you can: │ +│ • Select which services to INSTALL │ +│ • Select which services to UNINSTALL │ +│ • Skip services you don't want │ +│ │ +│ Services include: │ +│ • Self-hosted apps (ActualBudget, Keycloak, Jellyfin, etc.) │ +│ • Network services (Samba, VPNs, fail2ban) │ +│ • Monitoring tools (Uptime Kuma, Portainer, Watchtower) │ +│ • And many more... │ +└────────────────────────────────────────────────────────────────┘ + +┌────────────────────────────────────────────────────────────────┐ +│ RE-RUNNING THIS SCRIPT │ +├────────────────────────────────────────────────────────────────┤ +│ You can safely re-run this script on an already configured │ +│ server: │ +│ • Phase 1 will DETECT existing installations and skip them │ +│ • Phase 2 menu will show ALL services (installed and not) │ +│ • Select NEW services to add │ +│ • Or select UNINSTALL to remove services │ +│ │ +│ The script is IDEMPOTENT - safe to run multiple times! │ +└────────────────────────────────────────────────────────────────┘ + +Press ENTER to continue with Phase 1 (Essential Setup)... +EOF + +read -p "" + +# Now continue with the actual script... +EOF diff --git a/ubuntu-post-install.sh b/ubuntu-post-install.sh index 8a29d06..7a0d9e9 100644 --- a/ubuntu-post-install.sh +++ b/ubuntu-post-install.sh @@ -2186,35 +2186,122 @@ else # Use whiptail for service selection if available if command -v whiptail &> /dev/null; then - # Build checklist of all available services - SELECTED_SERVICES=$(whiptail --title "Select Docker Services to Install" \ - --checklist "Use SPACE to select, ENTER to confirm:" 25 78 17 \ - "IMMICH" "Photo & video backup (like Google Photos)" OFF \ - "AUDIOBOOKSHELF" "Audiobook & podcast server" OFF \ - "EMBY" "Media server for movies, TV, music" OFF \ - "ARM" "Automatic Ripping Machine for DVDs/Blu-rays" OFF \ - "FILEBROWSER" "Web-based file manager" OFF \ - "MAGICMIRROR" "Smart mirror / dashboard display" OFF \ - "ACTUALBUDGET" "Personal finance management with bank sync" OFF \ - "KEYCLOAK" "Identity & Access Management (SSO)" OFF \ - "CADDY" "Reverse proxy with automatic HTTPS" OFF \ - "FAIL2BAN" "Intrusion prevention system" OFF \ - "LYRION" "Music streaming server (LMS)" OFF \ - "MEALIE" "Recipe manager & meal planner" OFF \ - "MINECRAFT" "Minecraft game server" OFF \ - "JELLYFIN" "Free media server (Emby alternative)" OFF \ - "FRIGATE" "AI-powered NVR for security cameras" OFF \ - "NTFY" "Push notifications server" OFF \ - "UPTIMEKUMA" "Service monitoring dashboard" OFF \ - "WGEASY" "WireGuard VPN with web UI" OFF \ - "TRACCAR" "GPS tracking server" OFF \ - "PORTAINER" "Docker management web UI" OFF \ - "MESHCENTRAL" "Remote management server" OFF \ - "FINDMYDEVICE" "Device tracking (like Find My)" OFF \ - "FRIGATE_NOTIFY" "Push notifications for Frigate" OFF \ - "WATCHTOWER" "Automatic container updates" OFF \ + # Detect existing services + echo "Detecting existing services..." + declare -A EXISTING_SERVICES + [ -d "$DOCKER_DIR/immich" ] && EXISTING_SERVICES[IMMICH]="ON" + [ -d "$DOCKER_DIR/audiobookshelf" ] && EXISTING_SERVICES[AUDIOBOOKSHELF]="ON" + [ -d "$DOCKER_DIR/emby" ] && EXISTING_SERVICES[EMBY]="ON" + [ -d "$DOCKER_DIR/arm" ] && EXISTING_SERVICES[ARM]="ON" + [ -d "$DOCKER_DIR/filebrowser" ] && EXISTING_SERVICES[FILEBROWSER]="ON" + [ -d "$DOCKER_DIR/magicmirror" ] && EXISTING_SERVICES[MAGICMIRROR]="ON" + [ -d "$DOCKER_DIR/actualbudget" ] && EXISTING_SERVICES[ACTUALBUDGET]="ON" + [ -d "$DOCKER_DIR/keycloak" ] && EXISTING_SERVICES[KEYCLOAK]="ON" + [ -d "$DOCKER_DIR/caddy" ] && EXISTING_SERVICES[CADDY]="ON" + [ -d "$DOCKER_DIR/lms" ] && EXISTING_SERVICES[LYRION]="ON" + [ -d "$DOCKER_DIR/mealie" ] && EXISTING_SERVICES[MEALIE]="ON" + [ -d "$DOCKER_DIR/minecraft" ] && EXISTING_SERVICES[MINECRAFT]="ON" + [ -d "$DOCKER_DIR/jellyfin" ] && EXISTING_SERVICES[JELLYFIN]="ON" + [ -d "$DOCKER_DIR/frigate" ] && EXISTING_SERVICES[FRIGATE]="ON" + [ -d "$DOCKER_DIR/ntfy" ] && EXISTING_SERVICES[NTFY]="ON" + [ -d "$DOCKER_DIR/uptime-kuma" ] && EXISTING_SERVICES[UPTIMEKUMA]="ON" + [ -d "$DOCKER_DIR/wg-easy" ] && EXISTING_SERVICES[WGEASY]="ON" + [ -d "$DOCKER_DIR/traccar" ] && EXISTING_SERVICES[TRACCAR]="ON" + [ -d "$DOCKER_DIR/portainer" ] && EXISTING_SERVICES[PORTAINER]="ON" + [ -d "$DOCKER_DIR/meshcentral" ] && EXISTING_SERVICES[MESHCENTRAL]="ON" + [ -d "$DOCKER_DIR/fmd" ] && EXISTING_SERVICES[FINDMYDEVICE]="ON" + [ -d "$DOCKER_DIR/frigate-notify" ] && EXISTING_SERVICES[FRIGATE_NOTIFY]="ON" + [ -d "$DOCKER_DIR/watchtower" ] && EXISTING_SERVICES[WATCHTOWER]="ON" + command -v fail2ban-client &> /dev/null && EXISTING_SERVICES[FAIL2BAN]="ON" + + # Ask user what action to perform + ACTION=$(whiptail --title "Service Management" --menu \ + "Choose an action:" 15 60 3 \ + "1" "Install new services" \ + "2" "Uninstall existing services" \ + "3" "Cancel and skip" \ 3>&1 1>&2 2>&3) + case "$ACTION" in + 1) + # INSTALL MODE + MENU_MODE="INSTALL" + # Build checklist - mark existing services as ON + SELECTED_SERVICES=$(whiptail --title "Select Docker Services to Install" \ + --checklist "Use SPACE to select, ENTER to confirm. [*] = already installed" 25 78 17 \ + "IMMICH" "Photo & video backup (like Google Photos)" ${EXISTING_SERVICES[IMMICH]:-OFF} \ + "AUDIOBOOKSHELF" "Audiobook & podcast server" ${EXISTING_SERVICES[AUDIOBOOKSHELF]:-OFF} \ + "EMBY" "Media server for movies, TV, music" ${EXISTING_SERVICES[EMBY]:-OFF} \ + "ARM" "Automatic Ripping Machine for DVDs/Blu-rays" ${EXISTING_SERVICES[ARM]:-OFF} \ + "FILEBROWSER" "Web-based file manager" ${EXISTING_SERVICES[FILEBROWSER]:-OFF} \ + "MAGICMIRROR" "Smart mirror / dashboard display" ${EXISTING_SERVICES[MAGICMIRROR]:-OFF} \ + "ACTUALBUDGET" "Personal finance management with bank sync" ${EXISTING_SERVICES[ACTUALBUDGET]:-OFF} \ + "KEYCLOAK" "Identity & Access Management (SSO)" ${EXISTING_SERVICES[KEYCLOAK]:-OFF} \ + "CADDY" "Reverse proxy with automatic HTTPS" ${EXISTING_SERVICES[CADDY]:-OFF} \ + "FAIL2BAN" "Intrusion prevention system" ${EXISTING_SERVICES[FAIL2BAN]:-OFF} \ + "LYRION" "Music streaming server (LMS)" ${EXISTING_SERVICES[LYRION]:-OFF} \ + "MEALIE" "Recipe manager & meal planner" ${EXISTING_SERVICES[MEALIE]:-OFF} \ + "MINECRAFT" "Minecraft game server" ${EXISTING_SERVICES[MINECRAFT]:-OFF} \ + "JELLYFIN" "Free media server (Emby alternative)" ${EXISTING_SERVICES[JELLYFIN]:-OFF} \ + "FRIGATE" "AI-powered NVR for security cameras" ${EXISTING_SERVICES[FRIGATE]:-OFF} \ + "NTFY" "Push notifications server" ${EXISTING_SERVICES[NTFY]:-OFF} \ + "UPTIMEKUMA" "Service monitoring dashboard" ${EXISTING_SERVICES[UPTIMEKUMA]:-OFF} \ + "WGEASY" "WireGuard VPN with web UI" ${EXISTING_SERVICES[WGEASY]:-OFF} \ + "TRACCAR" "GPS tracking server" ${EXISTING_SERVICES[TRACCAR]:-OFF} \ + "PORTAINER" "Docker management web UI" ${EXISTING_SERVICES[PORTAINER]:-OFF} \ + "MESHCENTRAL" "Remote management server" ${EXISTING_SERVICES[MESHCENTRAL]:-OFF} \ + "FINDMYDEVICE" "Device tracking (like Find My)" ${EXISTING_SERVICES[FINDMYDEVICE]:-OFF} \ + "FRIGATE_NOTIFY" "Push notifications for Frigate" ${EXISTING_SERVICES[FRIGATE_NOTIFY]:-OFF} \ + "WATCHTOWER" "Automatic container updates" ${EXISTING_SERVICES[WATCHTOWER]:-OFF} \ + 3>&1 1>&2 2>&3) + ;; + 2) + # UNINSTALL MODE + MENU_MODE="UNINSTALL" + # Only show services that exist + UNINSTALL_OPTIONS="" + [ -n "${EXISTING_SERVICES[IMMICH]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS IMMICH \"Photo & video backup\" ON" + [ -n "${EXISTING_SERVICES[AUDIOBOOKSHELF]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS AUDIOBOOKSHELF \"Audiobook server\" ON" + [ -n "${EXISTING_SERVICES[EMBY]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS EMBY \"Media server\" ON" + [ -n "${EXISTING_SERVICES[ARM]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS ARM \"Automatic Ripping Machine\" ON" + [ -n "${EXISTING_SERVICES[FILEBROWSER]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS FILEBROWSER \"Web file manager\" ON" + [ -n "${EXISTING_SERVICES[MAGICMIRROR]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS MAGICMIRROR \"Smart mirror\" ON" + [ -n "${EXISTING_SERVICES[ACTUALBUDGET]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS ACTUALBUDGET \"Personal finance\" ON" + [ -n "${EXISTING_SERVICES[KEYCLOAK]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS KEYCLOAK \"Identity management\" ON" + [ -n "${EXISTING_SERVICES[CADDY]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS CADDY \"Reverse proxy\" ON" + [ -n "${EXISTING_SERVICES[FAIL2BAN]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS FAIL2BAN \"Intrusion prevention\" ON" + [ -n "${EXISTING_SERVICES[LYRION]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS LYRION \"Music server\" ON" + [ -n "${EXISTING_SERVICES[MEALIE]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS MEALIE \"Recipe manager\" ON" + [ -n "${EXISTING_SERVICES[MINECRAFT]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS MINECRAFT \"Game server\" ON" + [ -n "${EXISTING_SERVICES[JELLYFIN]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS JELLYFIN \"Media server\" ON" + [ -n "${EXISTING_SERVICES[FRIGATE]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS FRIGATE \"NVR cameras\" ON" + [ -n "${EXISTING_SERVICES[NTFY]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS NTFY \"Push notifications\" ON" + [ -n "${EXISTING_SERVICES[UPTIMEKUMA]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS UPTIMEKUMA \"Service monitoring\" ON" + [ -n "${EXISTING_SERVICES[WGEASY]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS WGEASY \"WireGuard VPN\" ON" + [ -n "${EXISTING_SERVICES[TRACCAR]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS TRACCAR \"GPS tracking\" ON" + [ -n "${EXISTING_SERVICES[PORTAINER]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS PORTAINER \"Docker management\" ON" + [ -n "${EXISTING_SERVICES[MESHCENTRAL]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS MESHCENTRAL \"Remote management\" ON" + [ -n "${EXISTING_SERVICES[FINDMYDEVICE]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS FINDMYDEVICE \"Device tracking\" ON" + [ -n "${EXISTING_SERVICES[FRIGATE_NOTIFY]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS FRIGATE_NOTIFY \"Frigate notifications\" ON" + [ -n "${EXISTING_SERVICES[WATCHTOWER]}" ] && UNINSTALL_OPTIONS="$UNINSTALL_OPTIONS WATCHTOWER \"Container updates\" ON" + + if [ -z "$UNINSTALL_OPTIONS" ]; then + whiptail --title "No Services Found" --msgbox "No services detected to uninstall." 8 50 + SELECTED_SERVICES="" + else + SELECTED_SERVICES=$(eval "whiptail --title 'Select Services to Uninstall' \ + --checklist 'WARNING: Data will be backed up but services will be removed' 25 78 17 \ + $UNINSTALL_OPTIONS \ + 3>&1 1>&2 2>&3") + fi + ;; + 3|*) + # Cancel + SELECTED_SERVICES="" + MENU_MODE="CANCEL" + ;; + esac + # Check if user cancelled if [ $? -ne 0 ]; then echo "Service selection cancelled. Skipping Docker applications." @@ -2274,14 +2361,134 @@ else if echo "$SELECTED_SERVICES" | grep -q "FRIGATE_NOTIFY"; then INSTALL_FRIGATE_NOTIFY="y"; fi if echo "$SELECTED_SERVICES" | grep -q "WATCHTOWER"; then INSTALL_WATCHTOWER="y"; fi - echo "" - echo "Selected services:" - echo "$SELECTED_SERVICES" | tr '"' '\n' | grep -v '^$' | sed 's/^/ - /' - echo "" + if [ "$MENU_MODE" = "UNINSTALL" ]; then + echo "" + echo "Services selected for uninstallation:" + echo "$SELECTED_SERVICES" | tr '"' '\n' | grep -v '^$' | sed 's/^/ - /' + echo "" + + # Process uninstalls + if [ -n "$SELECTED_SERVICES" ]; then + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " UNINSTALLING SERVICES" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + + # Define uninstall function + uninstall_service() { + local SERVICE_NAME=$1 + local SERVICE_DIR=$2 + local CONTAINER_NAME=$3 + + echo "Uninstalling $SERVICE_NAME..." + + if [ -d "$SERVICE_DIR" ]; then + cd "$SERVICE_DIR" 2>/dev/null || return + + # Stop and remove containers + if [ -f "docker-compose.yml" ]; then + echo " Stopping containers..." + docker compose down 2>/dev/null || docker-compose down 2>/dev/null || true + fi + + # Backup data + BACKUP_DIR="$DOCKER_DIR/backups/$(date +%Y%m%d-%H%M%S)-$SERVICE_NAME" + mkdir -p "$BACKUP_DIR" + echo " Backing up to $BACKUP_DIR..." + cp -r "$SERVICE_DIR" "$BACKUP_DIR/" 2>/dev/null || true + + # Remove service directory + echo " Removing $SERVICE_DIR..." + rm -rf "$SERVICE_DIR" + + echo " ✓ $SERVICE_NAME uninstalled (backup: $BACKUP_DIR)" + else + echo " ⚠ $SERVICE_NAME directory not found, skipping" + fi + echo "" + } + + # Uninstall selected services + if echo "$SELECTED_SERVICES" | grep -q "IMMICH"; then uninstall_service "Immich" "$DOCKER_DIR/immich" "immich"; fi + if echo "$SELECTED_SERVICES" | grep -q "AUDIOBOOKSHELF"; then uninstall_service "AudioBookshelf" "$DOCKER_DIR/audiobookshelf" "audiobookshelf"; fi + if echo "$SELECTED_SERVICES" | grep -q "EMBY"; then uninstall_service "Emby" "$DOCKER_DIR/emby" "emby"; fi + if echo "$SELECTED_SERVICES" | grep -q "ARM"; then uninstall_service "ARM" "$DOCKER_DIR/arm" "arm"; fi + if echo "$SELECTED_SERVICES" | grep -q "FILEBROWSER"; then uninstall_service "FileBrowser" "$DOCKER_DIR/filebrowser" "filebrowser"; fi + if echo "$SELECTED_SERVICES" | grep -q "MAGICMIRROR"; then uninstall_service "MagicMirror" "$DOCKER_DIR/magicmirror" "magicmirror"; fi + if echo "$SELECTED_SERVICES" | grep -q "ACTUALBUDGET"; then uninstall_service "ActualBudget" "$DOCKER_DIR/actualbudget" "actualbudget"; fi + if echo "$SELECTED_SERVICES" | grep -q "KEYCLOAK"; then uninstall_service "Keycloak" "$DOCKER_DIR/keycloak" "keycloak"; fi + if echo "$SELECTED_SERVICES" | grep -q "CADDY"; then uninstall_service "Caddy" "$DOCKER_DIR/caddy" "caddy"; fi + if echo "$SELECTED_SERVICES" | grep -q "LYRION"; then uninstall_service "Lyrion" "$DOCKER_DIR/lms" "lms"; fi + if echo "$SELECTED_SERVICES" | grep -q "MEALIE"; then uninstall_service "Mealie" "$DOCKER_DIR/mealie" "mealie"; fi + if echo "$SELECTED_SERVICES" | grep -q "MINECRAFT"; then uninstall_service "Minecraft" "$DOCKER_DIR/minecraft" "minecraft"; fi + if echo "$SELECTED_SERVICES" | grep -q "JELLYFIN"; then uninstall_service "Jellyfin" "$DOCKER_DIR/jellyfin" "jellyfin"; fi + if echo "$SELECTED_SERVICES" | grep -q "FRIGATE\""; then uninstall_service "Frigate" "$DOCKER_DIR/frigate" "frigate"; fi + if echo "$SELECTED_SERVICES" | grep -q "NTFY"; then uninstall_service "ntfy" "$DOCKER_DIR/ntfy" "ntfy"; fi + if echo "$SELECTED_SERVICES" | grep -q "UPTIMEKUMA"; then uninstall_service "Uptime Kuma" "$DOCKER_DIR/uptime-kuma" "uptime-kuma"; fi + if echo "$SELECTED_SERVICES" | grep -q "WGEASY"; then uninstall_service "wg-easy" "$DOCKER_DIR/wg-easy" "wg-easy"; fi + if echo "$SELECTED_SERVICES" | grep -q "TRACCAR"; then uninstall_service "Traccar" "$DOCKER_DIR/traccar" "traccar"; fi + if echo "$SELECTED_SERVICES" | grep -q "PORTAINER"; then uninstall_service "Portainer" "$DOCKER_DIR/portainer" "portainer"; fi + if echo "$SELECTED_SERVICES" | grep -q "MESHCENTRAL"; then uninstall_service "MeshCentral" "$DOCKER_DIR/meshcentral" "meshcentral"; fi + if echo "$SELECTED_SERVICES" | grep -q "FINDMYDEVICE"; then uninstall_service "FindMyDevice" "$DOCKER_DIR/fmd" "fmd"; fi + if echo "$SELECTED_SERVICES" | grep -q "FRIGATE_NOTIFY"; then uninstall_service "Frigate-Notify" "$DOCKER_DIR/frigate-notify" "frigate-notify"; fi + if echo "$SELECTED_SERVICES" | grep -q "WATCHTOWER"; then uninstall_service "Watchtower" "$DOCKER_DIR/watchtower" "watchtower"; fi + + # Special handling for fail2ban (system package) + if echo "$SELECTED_SERVICES" | grep -q "FAIL2BAN"; then + echo "Uninstalling fail2ban..." + echo " Stopping fail2ban service..." + systemctl stop fail2ban 2>/dev/null || true + systemctl disable fail2ban 2>/dev/null || true + echo " Removing fail2ban package..." + apt-get remove --purge -y fail2ban 2>/dev/null || true + echo " ✓ fail2ban uninstalled" + echo "" + fi + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " Uninstallation complete!" + echo " Backups saved to: $DOCKER_DIR/backups/" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + fi + + # After uninstall, exit the Docker apps section + WHIPTAIL_USED=true + + elif [ "$MENU_MODE" = "INSTALL" ]; then + echo "" + echo "Selected services:" + echo "$SELECTED_SERVICES" | tr '"' '\n' | grep -v '^$' | sed 's/^/ - /' + echo "" + + # Mark that whiptail was used - skip all individual prompts + WHIPTAIL_USED=true + else + # Cancel or other mode + WHIPTAIL_USED=false + fi + else + # Whiptail not available - will use individual prompts + WHIPTAIL_USED=false + MENU_MODE="INSTALL" fi - # ---- IMMICH ---- - if [ -z "$INSTALL_IMMICH" ]; then + # Skip installation section if we just did uninstalls + if [ "$MENU_MODE" = "UNINSTALL" ]; then + echo "Skipping installation section (uninstall mode was selected)." + # Jump to end of Docker section by setting a flag + SKIP_DOCKER_INSTALLS=true + else + SKIP_DOCKER_INSTALLS=false + fi + + # ============================================================================ + # DOCKER SERVICE INSTALLATIONS + # ============================================================================ + + if [ "$SKIP_DOCKER_INSTALLS" != true ]; then + # ---- IMMICH ---- + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_IMMICH" ]; then echo "" echo "┌─────────────────────────────────────────────────────────────────┐" echo "│ IMMICH - Self-hosted photo & video backup │" @@ -2491,13 +2698,15 @@ IMMICH_ENV fi # ---- AUDIOBOOKSHELF ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ AUDIOBOOKSHELF - Audiobook & podcast server │" - echo "│ Stream audiobooks with progress sync across devices. │" - echo "│ Port: 13378 │" - echo "└─────────────────────────────────────────────────────────────────┘" - prompt_yn "Install Audiobookshelf? (y/n):" "n" INSTALL_AUDIOBOOKSHELF + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_AUDIOBOOKSHELF" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ AUDIOBOOKSHELF - Audiobook & podcast server │" + echo "│ Stream audiobooks with progress sync across devices. │" + echo "│ Port: 13378 │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Audiobookshelf? (y/n):" "n" INSTALL_AUDIOBOOKSHELF + fi if [ "$INSTALL_AUDIOBOOKSHELF" = "y" ] || [ "$INSTALL_AUDIOBOOKSHELF" = "Y" ]; then echo "Installing Audiobookshelf..." @@ -2553,14 +2762,14 @@ ABS_ENV fi # ---- EMBY ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ EMBY - Media server for movies, TV, music │" - echo "│ Stream your media library to any device. │" - echo "│ Port: 8096 (web), 8920 (https) │" - echo "└─────────────────────────────────────────────────────────────────┘" - if [ -z "$INSTALL_EMBY" ]; then - prompt_yn "Install Emby? (y/n):" "n" INSTALL_EMBY + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_EMBY" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ EMBY - Media server for movies, TV, music │" + echo "│ Stream your media library to any device. │" + echo "│ Port: 8096 (web), 8920 (https) │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Emby? (y/n):" "n" INSTALL_EMBY fi if [ "$INSTALL_EMBY" = "y" ] || [ "$INSTALL_EMBY" = "Y" ]; then @@ -2620,14 +2829,14 @@ EMBY_ENV fi # ---- A.R.M. (Automatic Ripping Machine) ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ A.R.M. - Automatic Ripping Machine │" - echo "│ Automatically rip DVDs, Blu-rays, and CDs. │" - echo "│ Port: 8080 │" - echo "└─────────────────────────────────────────────────────────────────┘" - if [ -z "$INSTALL_ARM" ]; then - prompt_yn "Install A.R.M.? (y/n):" "n" INSTALL_ARM + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_ARM" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ A.R.M. - Automatic Ripping Machine │" + echo "│ Automatically rip DVDs, Blu-rays, and CDs. │" + echo "│ Port: 8080 │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install A.R.M.? (y/n):" "n" INSTALL_ARM fi if [ "$INSTALL_ARM" = "y" ] || [ "$INSTALL_ARM" = "Y" ]; then @@ -2704,14 +2913,14 @@ ARM_ENV fi # ---- FILEBROWSER ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ FILEBROWSER - Web-based file manager │" - echo "│ Browse, upload, download files via web interface. │" - echo "│ Port: 8085 │" - echo "└─────────────────────────────────────────────────────────────────┘" - if [ -z "$INSTALL_FILEBROWSER" ]; then - prompt_yn "Install Filebrowser? (y/n):" "n" INSTALL_FILEBROWSER + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_FILEBROWSER" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ FILEBROWSER - Web-based file manager │" + echo "│ Browse, upload, download files via web interface. │" + echo "│ Port: 8085 │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Filebrowser? (y/n):" "n" INSTALL_FILEBROWSER fi if [ "$INSTALL_FILEBROWSER" = "y" ] || [ "$INSTALL_FILEBROWSER" = "Y" ]; then @@ -2781,14 +2990,14 @@ FB_SETTINGS fi # ---- MAGIC MIRROR ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ MAGIC MIRROR - Smart mirror / dashboard display │" - echo "│ Modular smart mirror platform. Run up to 3 instances. │" - echo "│ Ports: 8081, 8082, 8083 │" - echo "└─────────────────────────────────────────────────────────────────┘" - if [ -z "$INSTALL_MAGICMIRROR" ]; then - prompt_yn "Install Magic Mirror? (y/n):" "n" INSTALL_MAGICMIRROR + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_MAGICMIRROR" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ MAGIC MIRROR - Smart mirror / dashboard display │" + echo "│ Modular smart mirror platform. Run up to 3 instances. │" + echo "│ Ports: 8081, 8082, 8083 │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Magic Mirror? (y/n):" "n" INSTALL_MAGICMIRROR fi if [ "$INSTALL_MAGICMIRROR" = "y" ] || [ "$INSTALL_MAGICMIRROR" = "Y" ]; then @@ -3014,13 +3223,13 @@ MM_CONFIG fi # ---- ACTUALBUDGET ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ ACTUALBUDGET - Open-source Personal Finance Management │" - echo "│ Budget tracking with bank account synchronization via SimpleFIN│" - echo "│ Port: 5006 │" - echo "└─────────────────────────────────────────────────────────────────┘" - if [ -z "$INSTALL_ACTUALBUDGET" ]; then + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_ACTUALBUDGET" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ ACTUALBUDGET - Open-source Personal Finance Management │" + echo "│ Budget tracking with bank account synchronization via SimpleFIN│" + echo "│ Port: 5006 │" + echo "└─────────────────────────────────────────────────────────────────┘" prompt_yn "Install ActualBudget? (y/n):" "n" INSTALL_ACTUALBUDGET fi @@ -3069,13 +3278,13 @@ AB_COMPOSE fi # ---- KEYCLOAK ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ KEYCLOAK - Identity and Access Management (IAM) │" - echo "│ SSO, OAuth2, SAML, User Management, MFA │" - echo "│ Port: 8180 (HTTP) - Use reverse proxy for HTTPS │" - echo "└─────────────────────────────────────────────────────────────────┘" - if [ -z "$INSTALL_KEYCLOAK" ]; then + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_KEYCLOAK" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ KEYCLOAK - Identity and Access Management (IAM) │" + echo "│ SSO, OAuth2, SAML, User Management, MFA │" + echo "│ Port: 8180 (HTTP) - Use reverse proxy for HTTPS │" + echo "└─────────────────────────────────────────────────────────────────┘" prompt_yn "Install Keycloak? (y/n):" "n" INSTALL_KEYCLOAK fi @@ -3587,13 +3796,13 @@ EOF fi # ---- CADDY WEB SERVER ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ CADDY - Modern Web Server & Reverse Proxy │" - echo "│ Automatic HTTPS, reverse proxy for all your services │" - echo "│ Port: 80 (HTTP), 443 (HTTPS) │" - echo "└─────────────────────────────────────────────────────────────────┘" - if [ -z "$INSTALL_CADDY" ]; then + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_CADDY" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ CADDY - Modern Web Server & Reverse Proxy │" + echo "│ Automatic HTTPS, reverse proxy for all your services │" + echo "│ Port: 80 (HTTP), 443 (HTTPS) │" + echo "└─────────────────────────────────────────────────────────────────┘" prompt_yn "Install Caddy reverse proxy? (y/n):" "n" INSTALL_CADDY fi @@ -3727,13 +3936,13 @@ CADDYFILE fi # ---- FAIL2BAN ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ FAIL2BAN - Intrusion Prevention System │" - echo "│ Automatically ban IPs with failed auth attempts │" - echo "│ Protects SSH, Caddy, and other services │" - echo "└─────────────────────────────────────────────────────────────────┘" - if [ -z "$INSTALL_FAIL2BAN" ]; then + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_FAIL2BAN" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ FAIL2BAN - Intrusion Prevention System │" + echo "│ Automatically ban IPs with failed auth attempts │" + echo "│ Protects SSH, Caddy, and other services │" + echo "└─────────────────────────────────────────────────────────────────┘" prompt_yn "Install and configure fail2ban? (y/n):" "n" INSTALL_FAIL2BAN fi @@ -3859,14 +4068,14 @@ backend = auto" fi # ---- LYRION MUSIC SERVER ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ LYRION MUSIC SERVER (LMS) - Music streaming server │" - echo "│ Stream music to Squeezebox devices, apps, and Chromecast. │" - echo "│ Port: 9000 (web), 9090 (CLI), 3483 (players) │" - echo "└─────────────────────────────────────────────────────────────────┘" - if [ -z "$INSTALL_LMS" ]; then - prompt_yn "Install Lyrion Music Server? (y/n):" "n" INSTALL_LMS + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_LMS" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ LYRION MUSIC SERVER (LMS) - Music streaming server │" + echo "│ Stream music to Squeezebox devices, apps, and Chromecast. │" + echo "│ Port: 9000 (web), 9090 (CLI), 3483 (players) │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Lyrion Music Server? (y/n):" "n" INSTALL_LMS fi if [ "$INSTALL_LMS" = "y" ] || [ "$INSTALL_LMS" = "Y" ]; then @@ -3925,14 +4134,14 @@ LMS_ENV fi # ---- MEALIE ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ MEALIE - Recipe manager & meal planner │" - echo "│ Save recipes, plan meals, generate shopping lists. │" - echo "│ Port: 9925 │" - echo "└─────────────────────────────────────────────────────────────────┘" - if [ -z "$INSTALL_MEALIE" ]; then - prompt_yn "Install Mealie? (y/n):" "n" INSTALL_MEALIE + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_MEALIE" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ MEALIE - Recipe manager & meal planner │" + echo "│ Save recipes, plan meals, generate shopping lists. │" + echo "│ Port: 9925 │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Mealie? (y/n):" "n" INSTALL_MEALIE fi if [ "$INSTALL_MEALIE" = "y" ] || [ "$INSTALL_MEALIE" = "Y" ]; then @@ -3986,14 +4195,14 @@ MEALIE_COMPOSE fi # ---- MINECRAFT SERVER ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ MINECRAFT SERVER - Game server with RAM limit │" - echo "│ Fabric server with configurable memory allocation. │" - echo "│ Port: 25565 │" - echo "└─────────────────────────────────────────────────────────────────┘" - if [ -z "$INSTALL_MINECRAFT" ]; then - prompt_yn "Install Minecraft Server? (y/n):" "n" INSTALL_MINECRAFT + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_MINECRAFT" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ MINECRAFT SERVER - Game server with RAM limit │" + echo "│ Fabric server with configurable memory allocation. │" + echo "│ Port: 25565 │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Minecraft Server? (y/n):" "n" INSTALL_MINECRAFT fi if [ "$INSTALL_MINECRAFT" = "y" ] || [ "$INSTALL_MINECRAFT" = "Y" ]; then @@ -4144,13 +4353,15 @@ MC_ENV fi # ---- JELLYFIN (Alternative to Emby) ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ JELLYFIN - Free media server (alternative to Emby) │" - echo "│ Stream movies, TV, music. No premium features locked. │" - echo "│ Port: 8096 │" - echo "└─────────────────────────────────────────────────────────────────┘" - prompt_yn "Install Jellyfin? (y/n):" "n" INSTALL_JELLYFIN + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_JELLYFIN" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ JELLYFIN - Free media server (alternative to Emby) │" + echo "│ Stream movies, TV, music. No premium features locked. │" + echo "│ Port: 8096 │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Jellyfin? (y/n):" "n" INSTALL_JELLYFIN + fi if [ "$INSTALL_JELLYFIN" = "y" ] || [ "$INSTALL_JELLYFIN" = "Y" ]; then echo "Installing Jellyfin..." @@ -4214,13 +4425,15 @@ JELLYFIN_ENV fi # ---- FRIGATE NVR ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ FRIGATE - AI-powered NVR for security cameras │" - echo "│ Object detection, recordings, 24/7 monitoring. │" - echo "│ Port: 5000 (web), 8554 (RTSP), 8555 (WebRTC) │" - echo "└─────────────────────────────────────────────────────────────────┘" - prompt_yn "Install Frigate? (y/n):" "n" INSTALL_FRIGATE + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_FRIGATE" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ FRIGATE - AI-powered NVR for security cameras │" + echo "│ Object detection, recordings, 24/7 monitoring. │" + echo "│ Port: 5000 (web), 8554 (RTSP), 8555 (WebRTC) │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Frigate? (y/n):" "n" INSTALL_FRIGATE + fi if [ "$INSTALL_FRIGATE" = "y" ] || [ "$INSTALL_FRIGATE" = "Y" ]; then echo "Installing Frigate..." @@ -4536,13 +4749,15 @@ CADDY_FILE fi # ---- DDCLIENT DYNAMIC DNS ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ DDCLIENT - Dynamic DNS updater │" - echo "│ Keep your domain pointing to your home IP. │" - echo "│ Supports: Cloudflare, DuckDNS, No-IP, and more. │" - echo "└─────────────────────────────────────────────────────────────────┘" - prompt_yn "Install ddclient? (y/n):" "n" INSTALL_DDCLIENT + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_DDCLIENT" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ DDCLIENT - Dynamic DNS updater │" + echo "│ Keep your domain pointing to your home IP. │" + echo "│ Supports: Cloudflare, DuckDNS, No-IP, and more. │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install ddclient? (y/n):" "n" INSTALL_DDCLIENT + fi if [ "$INSTALL_DDCLIENT" = "y" ] || [ "$INSTALL_DDCLIENT" = "Y" ]; then echo "Installing ddclient..." @@ -4622,13 +4837,15 @@ DDCLIENT_CONF fi # ---- NTFY NOTIFICATIONS ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ NTFY - Push notifications server │" - echo "│ Send notifications from scripts to your phone. │" - echo "│ Port: 8090 │" - echo "└─────────────────────────────────────────────────────────────────┘" - prompt_yn "Install ntfy? (y/n):" "n" INSTALL_NTFY + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_NTFY" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ NTFY - Push notifications server │" + echo "│ Send notifications from scripts to your phone. │" + echo "│ Port: 8090 │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install ntfy? (y/n):" "n" INSTALL_NTFY + fi if [ "$INSTALL_NTFY" = "y" ] || [ "$INSTALL_NTFY" = "Y" ]; then echo "Installing ntfy..." @@ -4683,13 +4900,15 @@ NTFY_ENV fi # ---- UPTIME KUMA ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ UPTIME KUMA - Service monitoring dashboard │" - echo "│ Monitor websites, servers, Docker containers. │" - echo "│ Port: 3001 │" - echo "└─────────────────────────────────────────────────────────────────┘" - prompt_yn "Install Uptime Kuma? (y/n):" "n" INSTALL_UPTIMEKUMA + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_UPTIMEKUMA" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ UPTIME KUMA - Service monitoring dashboard │" + echo "│ Monitor websites, servers, Docker containers. │" + echo "│ Port: 3001 │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Uptime Kuma? (y/n):" "n" INSTALL_UPTIMEKUMA + fi if [ "$INSTALL_UPTIMEKUMA" = "y" ] || [ "$INSTALL_UPTIMEKUMA" = "Y" ]; then echo "Installing Uptime Kuma..." @@ -4734,13 +4953,15 @@ UPTIME_COMPOSE fi # ---- WG-EASY (WireGuard with Web UI) ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ WG-EASY - WireGuard VPN with web management │" - echo "│ Easy WireGuard setup with QR codes for clients. │" - echo "│ Port: 51821 (web), 51820 (VPN) │" - echo "└─────────────────────────────────────────────────────────────────┘" - prompt_yn "Install wg-easy? (y/n):" "n" INSTALL_WGEASY + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_WGEASY" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ WG-EASY - WireGuard VPN with web management │" + echo "│ Easy WireGuard setup with QR codes for clients. │" + echo "│ Port: 51821 (web), 51820 (VPN) │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install wg-easy? (y/n):" "n" INSTALL_WGEASY + fi if [ "$INSTALL_WGEASY" = "y" ] || [ "$INSTALL_WGEASY" = "Y" ]; then echo "Installing wg-easy..." @@ -4808,13 +5029,15 @@ WGEASY_ENV fi # ---- TRACCAR GPS TRACKING ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ TRACCAR - GPS tracking server │" - echo "│ Track phones, vehicles, assets with OwnTracks/Traccar apps. │" - echo "│ Port: 8082 (web), 5055 (OsmAnd), 5000+ (devices) │" - echo "└─────────────────────────────────────────────────────────────────┘" - prompt_yn "Install Traccar? (y/n):" "n" INSTALL_TRACCAR + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_TRACCAR" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ TRACCAR - GPS tracking server │" + echo "│ Track phones, vehicles, assets with OwnTracks/Traccar apps. │" + echo "│ Port: 8082 (web), 5055 (OsmAnd), 5000+ (devices) │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Traccar? (y/n):" "n" INSTALL_TRACCAR + fi if [ "$INSTALL_TRACCAR" = "y" ] || [ "$INSTALL_TRACCAR" = "Y" ]; then echo "Installing Traccar..." @@ -4879,13 +5102,15 @@ TRACCAR_XML fi # ---- PORTAINER ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ PORTAINER - Docker management web UI │" - echo "│ Manage containers, images, volumes via browser. │" - echo "│ Port: 9443 (https), 9000 (http) │" - echo "└─────────────────────────────────────────────────────────────────┘" - prompt_yn "Install Portainer? (y/n):" "n" INSTALL_PORTAINER + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_PORTAINER" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ PORTAINER - Docker management web UI │" + echo "│ Manage containers, images, volumes via browser. │" + echo "│ Port: 9443 (https), 9000 (http) │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Portainer? (y/n):" "n" INSTALL_PORTAINER + fi if [ "$INSTALL_PORTAINER" = "y" ] || [ "$INSTALL_PORTAINER" = "Y" ]; then echo "Installing Portainer..." @@ -4932,13 +5157,15 @@ PORTAINER_COMPOSE fi # ---- MESHCENTRAL SERVER ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ MESHCENTRAL SERVER - Self-hosted remote management │" - echo "│ Full MeshCentral server (not just agent). Manage all devices. │" - echo "│ Port: 4430 (https), 4433 (agent) │" - echo "└─────────────────────────────────────────────────────────────────┘" - prompt_yn "Install MeshCentral Server? (y/n):" "n" INSTALL_MESHCENTRAL_SERVER + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_MESHCENTRAL_SERVER" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ MESHCENTRAL SERVER - Self-hosted remote management │" + echo "│ Full MeshCentral server (not just agent). Manage all devices. │" + echo "│ Port: 4430 (https), 4433 (agent) │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install MeshCentral Server? (y/n):" "n" INSTALL_MESHCENTRAL_SERVER + fi if [ "$INSTALL_MESHCENTRAL_SERVER" = "y" ] || [ "$INSTALL_MESHCENTRAL_SERVER" = "Y" ]; then echo "Installing MeshCentral Server..." @@ -5010,13 +5237,15 @@ MC_ENV fi # ---- FINDMYDEVICE (FMD) ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ FINDMYDEVICE - Self-hosted device tracking │" - echo "│ Track and locate Android devices. Alternative to Google Find. │" - echo "│ Port: 8084 │" - echo "└─────────────────────────────────────────────────────────────────┘" - prompt_yn "Install FindMyDevice server? (y/n):" "n" INSTALL_FMD + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_FMD" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ FINDMYDEVICE - Self-hosted device tracking │" + echo "│ Track and locate Android devices. Alternative to Google Find. │" + echo "│ Port: 8084 │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install FindMyDevice server? (y/n):" "n" INSTALL_FMD + fi if [ "$INSTALL_FMD" = "y" ] || [ "$INSTALL_FMD" = "Y" ]; then echo "Installing FindMyDevice..." @@ -5073,13 +5302,15 @@ FMD_ENV fi # ---- FRIGATE-NOTIFY ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ FRIGATE-NOTIFY - Push notifications for Frigate events │" - echo "│ Get alerts when Frigate detects people, cars, etc. │" - echo "│ Sends to: ntfy, Pushover, Discord, Gotify, and more. │" - echo "└─────────────────────────────────────────────────────────────────┘" - prompt_yn "Install Frigate-Notify? (y/n):" "n" INSTALL_FRIGATE_NOTIFY + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_FRIGATE_NOTIFY" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ FRIGATE-NOTIFY - Push notifications for Frigate events │" + echo "│ Get alerts when Frigate detects people, cars, etc. │" + echo "│ Sends to: ntfy, Pushover, Discord, Gotify, and more. │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Frigate-Notify? (y/n):" "n" INSTALL_FRIGATE_NOTIFY + fi if [ "$INSTALL_FRIGATE_NOTIFY" = "y" ] || [ "$INSTALL_FRIGATE_NOTIFY" = "Y" ]; then echo "Installing Frigate-Notify..." @@ -5176,13 +5407,15 @@ FN_CONFIG fi # ---- WATCHTOWER ---- - echo "" - echo "┌─────────────────────────────────────────────────────────────────┐" - echo "│ WATCHTOWER - Container update monitoring │" - echo "│ Monitor containers for updates. NOTIFY ONLY by default. │" - echo "│ Why notify-only? Apps like Immich have breaking DB migrations. │" - echo "└─────────────────────────────────────────────────────────────────┘" - prompt_yn "Install Watchtower? (y/n):" "n" INSTALL_WATCHTOWER + if [ "$WHIPTAIL_USED" != true ] && [ -z "$INSTALL_WATCHTOWER" ]; then + echo "" + echo "┌─────────────────────────────────────────────────────────────────┐" + echo "│ WATCHTOWER - Container update monitoring │" + echo "│ Monitor containers for updates. NOTIFY ONLY by default. │" + echo "│ Why notify-only? Apps like Immich have breaking DB migrations. │" + echo "└─────────────────────────────────────────────────────────────────┘" + prompt_yn "Install Watchtower? (y/n):" "n" INSTALL_WATCHTOWER + fi if [ "$INSTALL_WATCHTOWER" = "y" ] || [ "$INSTALL_WATCHTOWER" = "Y" ]; then echo "Installing Watchtower..." @@ -5301,6 +5534,8 @@ WT_ENV fi fi + fi # End SKIP_DOCKER_INSTALLS check + # ============================================================================ # KOPIA BACKUP FOR DOCKER CONTAINERS # ============================================================================