diff --git a/scripts/docker-setup.sh b/scripts/docker-setup.sh index 2b03eb1..e4f5eb1 100644 --- a/scripts/docker-setup.sh +++ b/scripts/docker-setup.sh @@ -1,16 +1,18 @@ #!/bin/bash #=============================================================================== -# Docker + Felhom Infrastructure Setup v4.0 +# Felhom Infrastructure Setup v5.0 # Prepares a Debian 13 server for Felhom homeserver deployment # -# This script sets up the infrastructure: +# This script sets up the complete infrastructure: # - Docker Engine + Compose # - Traefik reverse proxy # - TLS certificates (Let's Encrypt via Cloudflare DNS or self-signed) -# - Cloudflare Tunnel connector (optional, for external access) -# - FileBrowser Quantum (web-based file manager for HDD data) -# - Felhom Controller (deployed separately) +# - Interactive configuration wizard (generates controller.yaml) +# - Cloudflare Tunnel connector (optional, configured via wizard) +# - FileBrowser Quantum (web-based file manager) +# - Felhom Controller (automatic deployment + dashboard) +# - Helper tools (ctop, lazydocker, shell aliases) # # Application stacks are managed via the felhom-controller dashboard. # @@ -24,10 +26,10 @@ # --gateway ADDRESS Gateway address (default: 192.168.0.1) # --dns ADDRESS DNS server (default: 1.1.1.1,8.8.8.8) # --interface NAME Network interface (default: auto-detect) -# --domain DOMAIN Base domain for services (default: homeserver.local) -# --email EMAIL Email for Let's Encrypt certificates -# --cf-token TOKEN Cloudflare API token for DNS-01 challenge -# --cf-tunnel-token TK Cloudflare Tunnel token for external access +# --domain DOMAIN Base domain (pre-seeds wizard) +# --email EMAIL ACME email (pre-seeds wizard) +# --cf-token TOKEN Cloudflare API token (pre-seeds wizard) +# --customer ID Customer identifier (pre-seeds wizard) # --traefik-password PW Password for Traefik dashboard (default: auto-generated) # --self-signed-cert Generate self-signed wildcard certificate # --skip-filebrowser Skip FileBrowser installation @@ -37,7 +39,7 @@ # # Example: # sudo ./docker-setup.sh --domain demo-felhom.eu --customer demo-felhom \ -# --email certs@felhom.eu --cf-token cf-xxx --cf-tunnel-token eyJhIjo... +# --email certs@felhom.eu --cf-token cf-xxx # #=============================================================================== @@ -117,7 +119,7 @@ done #------------------------------------------------------------------------------- # Configuration #------------------------------------------------------------------------------- -SCRIPT_VERSION="3.6.0" +SCRIPT_VERSION="5.0.0" # Default values STATIC_IP="" @@ -127,10 +129,8 @@ INTERFACE="" BASE_DOMAIN="homeserver.local" ACME_EMAIL="" CF_DNS_API_TOKEN="" -CF_TUNNEL_TOKEN="" TRAEFIK_PASSWORD="" SKIP_FILEBROWSER=false -HDD_PATH="" DRY_RUN=false SELF_SIGNED_CERT=false DEBUG_MODE=false @@ -183,17 +183,18 @@ trap 'on_error $LINENO' ERR print_banner() { echo "" echo -e "${BOLD}${BLUE}-==================================================================¬${NC}" - echo -e "${BOLD}${BLUE}¦ Docker + Felhom Infrastructure Setup v${SCRIPT_VERSION} ¦${NC}" + echo -e "${BOLD}${BLUE}¦ Felhom Infrastructure Setup v${SCRIPT_VERSION} ¦${NC}" echo -e "${BOLD}${BLUE}L==================================================================-${NC}" echo "" } print_help() { cat << 'EOF' -Docker + Felhom Infrastructure Setup v4.0 +Felhom Infrastructure Setup v5.0 -This script prepares a Debian 13 server with Docker infrastructure for Felhom homeserver. -Application stacks are managed via the felhom-controller dashboard. +Prepares a Debian 13 server for Felhom homeserver deployment. Installs +infrastructure, runs an interactive configuration wizard, generates +controller.yaml, and deploys felhom-controller — all in one run. USAGE: ./docker-setup.sh --bootstrap # First, on fresh Debian @@ -201,16 +202,14 @@ USAGE: OPTIONS: --bootstrap Install sudo (run first on fresh Debian) - --customer ID Customer identifier (e.g., demo-felhom) + --customer ID Customer identifier (pre-seeds wizard) --ip ADDRESS Static IP address --gateway ADDRESS Gateway (default: 192.168.0.1) --dns ADDRESS DNS servers, comma-separated (default: 1.1.1.1,8.8.8.8) --interface NAME Network interface (default: auto-detect) - --domain DOMAIN Base domain for services (default: homeserver.local) - --email EMAIL Email for Let's Encrypt certificates - --cf-token TOKEN Cloudflare API token for DNS-01 challenge (recommended) - --cf-tunnel-token TK Cloudflare Tunnel token for external access - --hdd-path PATH HDD mount path (e.g., /mnt/hdd_1) for FileBrowser + --domain DOMAIN Base domain for services (pre-seeds wizard) + --email EMAIL Email for Let's Encrypt (pre-seeds wizard) + --cf-token TOKEN Cloudflare API token for DNS-01 (pre-seeds wizard) --traefik-password PW Password for Traefik dashboard (default: auto-generated) --self-signed-cert Generate self-signed wildcard certificate (fallback) --skip-filebrowser Skip FileBrowser installation @@ -218,12 +217,14 @@ OPTIONS: --debug Enable verbose debug output -h, --help Show this help + CLI options pre-seed the interactive wizard — values can be changed + during the wizard prompts. + TLS CERTIFICATE OPTIONS: There are three TLS modes (in order of preference): 1. Let's Encrypt + Cloudflare DNS (recommended for Felhom customers): --email certs@felhom.eu --cf-token - Uses DNS-01 challenge. Works with Cloudflare Tunnel, no port 80 needed. 2. Let's Encrypt + HTTP-01 (public servers without Cloudflare Tunnel): --email admin@example.com @@ -231,38 +232,31 @@ TLS CERTIFICATE OPTIONS: 3. Self-signed certificate (offline / no internet): --self-signed-cert - Generates a CA + wildcard cert. Requires manual CA import on all devices. WHAT THIS SCRIPT INSTALLS: 1. Base packages (curl, git, htop, etc.) 2. Docker Engine + Docker Compose 3. Traefik reverse proxy (with dashboard) - 4. Cloudflare Tunnel connector (if --cf-tunnel-token provided) - 5. TLS certificates (Let's Encrypt or self-signed) + 4. TLS certificates (Let's Encrypt or self-signed) + 5. Felhom Controller (with interactive configuration wizard) 6. FileBrowser Quantum (web file manager at files.) - 7. Helper tools (ctop, lazydocker, shell aliases) - -DEPLOYING APPLICATIONS: - After infrastructure setup, deploy the felhom-controller to manage apps - via the web dashboard at felhom.. + 7. Cloudflare Tunnel (if configured in wizard) + 8. Helper tools (ctop, lazydocker, shell aliases) EXAMPLES: - # Felhom customer deployment (recommended — full stack with tunnel) - sudo ./docker-setup.sh --domain demo-felhom.eu --customer demo-felhom \ - --email certs@felhom.eu --cf-token cf-xxxxxxxxxxxx \ - --cf-tunnel-token eyJhIjoixxxxxxxx... - - # Without tunnel (local access only, or custom ingress) + # Felhom customer deployment (recommended) sudo ./docker-setup.sh --domain demo-felhom.eu --customer demo-felhom \ --email certs@felhom.eu --cf-token cf-xxxxxxxxxxxx + # Minimal (wizard will prompt for everything) + sudo ./docker-setup.sh + # Self-signed cert (offline/testing) sudo ./docker-setup.sh --domain example.com --self-signed-cert # Full setup with static IP sudo ./docker-setup.sh --domain demo-felhom.eu --customer demo-felhom \ - --ip 192.168.0.50 --email certs@felhom.eu --cf-token cf-xxxxxxxxxxxx \ - --cf-tunnel-token eyJhIjoixxxxxxxx... + --ip 192.168.0.50 --email certs@felhom.eu --cf-token cf-xxxxxxxxxxxx EOF } @@ -301,9 +295,6 @@ parse_args() { --cf-token) require_arg "$1" "${2:-}" CF_DNS_API_TOKEN="$2"; shift 2 ;; - --cf-tunnel-token) - require_arg "$1" "${2:-}" - CF_TUNNEL_TOKEN="$2"; shift 2 ;; --traefik-password) require_arg "$1" "${2:-}" TRAEFIK_PASSWORD="$2"; shift 2 ;; @@ -312,9 +303,6 @@ parse_args() { CUSTOMER_ID="$2"; shift 2 ;; --self-signed-cert) SELF_SIGNED_CERT=true; shift ;; --skip-filebrowser) SKIP_FILEBROWSER=true; shift ;; - --hdd-path) - require_arg "$1" "${2:-}" - HDD_PATH="$2"; shift 2 ;; --dry-run) DRY_RUN=true; shift ;; --debug) DEBUG_MODE=true; shift ;; -h|--help) print_help; exit 0 ;; @@ -373,12 +361,6 @@ parse_args() { log_warn "Let's Encrypt will be used; self-signed cert will serve as fallback" fi - # Warn if tunnel token provided without Cloudflare DNS (unusual but valid) - if [[ -n "$CF_TUNNEL_TOKEN" && -z "$CF_DNS_API_TOKEN" ]]; then - log_warn "Cloudflare Tunnel without DNS-01 challenge" - log_warn "External HTTPS will work, but certs must be managed separately" - fi - # Validate CUSTOMER_ID format if provided if [[ -n "$CUSTOMER_ID" ]]; then if [[ ! "$CUSTOMER_ID" =~ ^[a-zA-Z0-9_-]+$ ]]; then @@ -454,9 +436,8 @@ detect_network_manager_debug() { # Calculate total number of steps dynamically get_total_steps() { - local total=5 # base: packages, network, docker, traefik, tools - [[ -n "$CF_TUNNEL_TOKEN" ]] && ((total++)) - [[ "$SELF_SIGNED_CERT" == true ]] && ((total++)) + local total=8 # base: packages, network, docker, traefik, cert, wizard, controller, tools + [[ -n "${WIZ_CF_TUNNEL_TOKEN:-}" ]] && ((total++)) [[ "$SKIP_FILEBROWSER" != true ]] && ((total++)) echo "$total" } @@ -1039,12 +1020,14 @@ EOF # Step 4b: Install Cloudflare Tunnel connector (optional) #------------------------------------------------------------------------------- install_cloudflare_tunnel() { - if [[ -z "$CF_TUNNEL_TOKEN" ]]; then - log_skip "Cloudflare Tunnel not configured (no --cf-tunnel-token)" + if [[ -z "${WIZ_CF_TUNNEL_TOKEN:-}" ]]; then + log_skip "Cloudflare Tunnel not configured (skipped in wizard)" return fi - log_step "5/$(get_total_steps) - Installing Cloudflare Tunnel connector..." + local step_num=6 + [[ "$SELF_SIGNED_CERT" == true ]] && ((step_num++)) + log_step "${step_num}/$(get_total_steps) - Installing Cloudflare Tunnel connector..." if [[ "$DRY_RUN" == true ]]; then echo -e "${CYAN}[DRY-RUN]${NC} Would deploy cloudflared tunnel connector" @@ -1069,7 +1052,7 @@ services: restart: unless-stopped command: tunnel run environment: - - TUNNEL_TOKEN=${CF_TUNNEL_TOKEN} + - TUNNEL_TOKEN=${WIZ_CF_TUNNEL_TOKEN} dns: - 1.1.1.1 - 8.8.8.8 @@ -1115,10 +1098,7 @@ generate_self_signed_cert() { return fi - # Calculate step number dynamically - local step_num=5 - [[ -n "$CF_TUNNEL_TOKEN" ]] && ((step_num++)) - log_step "${step_num}/$(get_total_steps) - Generating self-signed certificate..." + log_step "5/$(get_total_steps) - Generating self-signed certificate..." if [[ "$DRY_RUN" == true ]]; then echo -e "${CYAN}[DRY-RUN]${NC} Would generate self-signed cert for *.${BASE_DOMAIN}" @@ -1234,7 +1214,7 @@ EOF } #------------------------------------------------------------------------------- -# Step 6: Install FileBrowser Quantum (infrastructure file manager) +# Install FileBrowser Quantum (infrastructure file manager) #------------------------------------------------------------------------------- install_filebrowser() { if [[ "$SKIP_FILEBROWSER" == true ]]; then @@ -1242,26 +1222,52 @@ install_filebrowser() { return fi - if [[ -z "$HDD_PATH" ]]; then - log_warn "FileBrowser skipped — no HDD path configured (use --hdd-path)." - log_warn "Deploy manually after HDD setup." - return - fi - - if [[ ! -d "$HDD_PATH" ]]; then - log_warn "FileBrowser skipped — HDD path $HDD_PATH does not exist." - log_warn "Run hdd-setup.sh first, then re-run with --hdd-path." - return - fi - # Calculate step number dynamically - local step_num=5 - [[ -n "$CF_TUNNEL_TOKEN" ]] && ((step_num++)) + local step_num=6 [[ "$SELF_SIGNED_CERT" == true ]] && ((step_num++)) + [[ -n "${WIZ_CF_TUNNEL_TOKEN:-}" ]] && ((step_num++)) log_step "${step_num}/$(get_total_steps) - Installing FileBrowser Quantum..." + # Discover drive mounts for FileBrowser volumes + local volume_lines="" + local mount_comment="" + local found_mounts=0 + + # Scan /mnt/ for existing mount points (e.g., /mnt/hdd_1, /mnt/sys_drive) + if [[ -d /mnt ]]; then + for mp in /mnt/*/; do + [[ ! -d "$mp" ]] && continue + local name + name=$(basename "$mp") + # Skip hidden dirs and raw mount dirs + [[ "$name" == .* ]] && continue + [[ "$name" == .felhom-raw ]] && continue + volume_lines+=" - ${mp%/}:/srv/${name}"$'\n' + mount_comment+=" # ${mp%/} → /srv/${name}"$'\n' + ((found_mounts++)) + done + fi + + # Also add system_data_path from wizard if it's not already in /mnt + if [[ -n "${WIZ_SYSTEM_DATA_PATH:-}" && -d "${WIZ_SYSTEM_DATA_PATH}" ]]; then + local sdp_name + sdp_name=$(basename "${WIZ_SYSTEM_DATA_PATH}") + if ! echo "$volume_lines" | grep -q "${WIZ_SYSTEM_DATA_PATH}"; then + volume_lines+=" - ${WIZ_SYSTEM_DATA_PATH}:/srv/${sdp_name}"$'\n' + ((found_mounts++)) + fi + fi + + if [[ $found_mounts -eq 0 ]]; then + log_warn "No mount points found in /mnt/ — FileBrowser will have no drive volumes." + log_warn "Drives can be attached later via the controller dashboard." + fi + if [[ "$DRY_RUN" == true ]]; then echo -e "${CYAN}[DRY-RUN]${NC} Would install FileBrowser Quantum at files.${BASE_DOMAIN}" + if [[ $found_mounts -gt 0 ]]; then + echo -e "${CYAN}[DRY-RUN]${NC} Would mount ${found_mounts} drive(s)" + fi return fi @@ -1277,12 +1283,7 @@ install_filebrowser() { cat > "${FILEBROWSER_DIR}/docker-compose.yml" << EOF # FileBrowser Quantum — Infrastructure file manager # Domain: files.${BASE_DOMAIN} -# Deployed by docker-setup.sh — do NOT remove -# -# Mount permissions: -# /srv/appdata/ → HDD appdata/ (READ-ONLY — app data) -# /srv/media/ → HDD media/ (read-write — user media) -# /srv/Dokumentumok/ → HDD Dokumentumok/ (read-write — user documents) +# Deployed by docker-setup.sh v${SCRIPT_VERSION} services: filebrowser: @@ -1293,10 +1294,7 @@ services: - TZ=Europe/Budapest volumes: - filebrowser_data:/home/filebrowser/data - - \${HDD_PATH}/appdata:/srv/appdata:ro - - \${HDD_PATH}/media:/srv/media - - \${HDD_PATH}/Dokumentumok:/srv/Dokumentumok - networks: +${volume_lines} networks: - traefik-public deploy: resources: @@ -1310,7 +1308,7 @@ services: start_period: 15s labels: - "traefik.enable=true" - - "traefik.http.routers.filebrowser.rule=Host(\`files.\${DOMAIN}\`)" + - "traefik.http.routers.filebrowser.rule=Host(\`files.${BASE_DOMAIN}\`)" - "traefik.http.routers.filebrowser.entrypoints=websecure" - "traefik.http.routers.filebrowser.tls=true" ${fb_certresolver_label} @@ -1351,13 +1349,6 @@ app_info: - Külső HDD csatlakoztatva és felcsatolva METAEOF - # Create .env file - cat > "${FILEBROWSER_DIR}/.env" << ENVEOF -# FileBrowser environment — generated by docker-setup.sh -DOMAIN=${BASE_DOMAIN} -HDD_PATH=${HDD_PATH} -ENVEOF - cd "${FILEBROWSER_DIR}" log_info "Pulling FileBrowser image..." docker compose pull -q @@ -1438,13 +1429,354 @@ EOF } +#------------------------------------------------------------------------------- +# Configuration wizard — generates controller.yaml +#------------------------------------------------------------------------------- +# Wizard variables (populated by prompts, used by later steps) +WIZ_CUSTOMER_ID="" +WIZ_CUSTOMER_NAME="" +WIZ_DOMAIN="" +WIZ_EMAIL="" +WIZ_CF_TUNNEL_TOKEN="" +WIZ_CF_API_TOKEN="" +WIZ_SYSTEM_DATA_PATH="" +WIZ_PASSWORD_HASH="" +WIZ_SESSION_SECRET="" +WIZ_GIT_REPO="" +WIZ_GIT_USERNAME="" +WIZ_GIT_TOKEN="" +WIZ_HC_HEARTBEAT="" +WIZ_HC_SYSTEM="" +WIZ_HC_DBDUMP="" +WIZ_HC_BACKUP="" +WIZ_HC_INTEGRITY="" + +CONTROLLER_DIR="/opt/docker/felhom-controller" + +run_config_wizard() { + local step_num=5 + [[ "$SELF_SIGNED_CERT" == true ]] && ((step_num++)) + log_step "${step_num}/$(get_total_steps) - Running configuration wizard..." + + echo "" + echo -e "${BOLD}${CYAN}===========================================================${NC}" + echo -e "${BOLD}${CYAN} Felhom Controller Configuration Wizard${NC}" + echo -e "${BOLD}${CYAN}===========================================================${NC}" + echo "" + + # Pre-seed from CLI flags + local def_customer="${CUSTOMER_ID}" + local def_domain="${BASE_DOMAIN}" + local def_email="${ACME_EMAIL}" + local def_cf_api="${CF_DNS_API_TOKEN}" + + # --- Customer identity --- + echo -e "${BOLD}--- Customer identity ---${NC}" + read -rp "Customer ID [${def_customer:-demo-felhom}]: " input + WIZ_CUSTOMER_ID="${input:-${def_customer:-demo-felhom}}" + + read -rp "Customer display name [${WIZ_CUSTOMER_ID}]: " input + WIZ_CUSTOMER_NAME="${input:-${WIZ_CUSTOMER_ID}}" + + read -rp "Domain [${def_domain}]: " input + WIZ_DOMAIN="${input:-${def_domain}}" + + read -rp "Customer email (optional) [${def_email}]: " input + WIZ_EMAIL="${input:-${def_email}}" + echo "" + + # --- Infrastructure secrets --- + echo -e "${BOLD}--- Infrastructure secrets ---${NC}" + read -rp "Cloudflare Tunnel token (optional, leave empty to skip) []: " WIZ_CF_TUNNEL_TOKEN + + read -rp "Cloudflare API token (for DNS-01 certs, optional) [${def_cf_api}]: " input + WIZ_CF_API_TOKEN="${input:-${def_cf_api}}" + echo "" + + # --- Paths --- + echo -e "${BOLD}--- Paths ---${NC}" + echo " System data partition mount point" + echo " (if the system drive was partitioned for user data," + echo " provide the mount point, e.g., /mnt/sys_drive)" + read -rp "System data path [/mnt/sys_drive]: " input + WIZ_SYSTEM_DATA_PATH="${input:-/mnt/sys_drive}" + echo "" + + # --- Dashboard password --- + echo -e "${BOLD}--- Dashboard password ---${NC}" + echo " Set a password for the controller dashboard." + echo " (leave empty for first-visit setup prompt)" + read -rsp "Dashboard password []: " wiz_password + echo "" + if [[ -n "$wiz_password" ]]; then + # Hash with htpasswd (apache2-utils installed in step 1) + if command -v htpasswd &>/dev/null; then + WIZ_PASSWORD_HASH=$(htpasswd -bnBC 10 "" "$wiz_password" | tr -d ':\n') + elif command -v python3 &>/dev/null; then + WIZ_PASSWORD_HASH=$(python3 -c "import bcrypt; print(bcrypt.hashpw(b'${wiz_password}', bcrypt.gensalt(10)).decode())" 2>/dev/null || echo "") + fi + if [[ -z "$WIZ_PASSWORD_HASH" ]]; then + log_warn "Could not hash password — dashboard will prompt on first visit" + fi + fi + echo "" + + # Generate session secret + WIZ_SESSION_SECRET=$(openssl rand -hex 32) + + # --- Git sync --- + echo -e "${BOLD}--- Git sync ---${NC}" + read -rp "App catalog repository URL [https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git]: " input + WIZ_GIT_REPO="${input:-https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git}" + + read -rp "Git username []: " WIZ_GIT_USERNAME + read -rp "Git token []: " WIZ_GIT_TOKEN + echo "" + + # --- Healthcheck monitoring --- + echo -e "${BOLD}--- Healthcheck monitoring ---${NC}" + echo " Healthchecks.io ping UUIDs (leave empty to skip):" + read -rp " Heartbeat UUID []: " WIZ_HC_HEARTBEAT + read -rp " System health UUID []: " WIZ_HC_SYSTEM + read -rp " DB dump UUID []: " WIZ_HC_DBDUMP + read -rp " Backup UUID []: " WIZ_HC_BACKUP + read -rp " Backup integrity UUID []: " WIZ_HC_INTEGRITY + echo "" + + # Update global variables that other steps depend on + BASE_DOMAIN="${WIZ_DOMAIN}" + CUSTOMER_ID="${WIZ_CUSTOMER_ID}" + CF_DNS_API_TOKEN="${WIZ_CF_API_TOKEN}" + ACME_EMAIL="${WIZ_EMAIL}" + + # --- Generate controller.yaml --- + if [[ "$DRY_RUN" == true ]]; then + echo -e "${CYAN}[DRY-RUN]${NC} Would generate ${CONTROLLER_DIR}/controller.yaml" + echo -e "${CYAN}[DRY-RUN]${NC} Customer: ${WIZ_CUSTOMER_ID}, Domain: ${WIZ_DOMAIN}" + return + fi + + mkdir -p "${CONTROLLER_DIR}" + + cat > "${CONTROLLER_DIR}/controller.yaml" << YAMLEOF +# Felhom Controller Configuration +# Generated by docker-setup.sh v${SCRIPT_VERSION} on $(date -u +"%Y-%m-%dT%H:%M:%SZ") + +customer: + id: "${WIZ_CUSTOMER_ID}" + name: "${WIZ_CUSTOMER_NAME}" + domain: "${WIZ_DOMAIN}" + email: "${WIZ_EMAIL}" + telegram_chat_id: "" + +infrastructure: + cf_tunnel_token: "${WIZ_CF_TUNNEL_TOKEN}" + cf_api_token: "${WIZ_CF_API_TOKEN}" + +paths: + stacks_dir: "/opt/docker/stacks" + data_dir: "/opt/docker/felhom-controller/data" + system_data_path: "${WIZ_SYSTEM_DATA_PATH}" + +system: + reserved_memory_mb: 384 + +web: + listen: ":8080" + password_hash: "${WIZ_PASSWORD_HASH}" + session_secret: "${WIZ_SESSION_SECRET}" + +git: + repo_url: "${WIZ_GIT_REPO}" + branch: "main" + sync_interval: "15m" + username: "${WIZ_GIT_USERNAME}" + token: "${WIZ_GIT_TOKEN}" + +stacks: + protected: + - "traefik" + - "cloudflared" + - "felhom-controller" + - "filebrowser" + update_window: "03:00-05:00" + compose_command: "" + +backup: + enabled: true + restic_password_file: "/opt/docker/felhom-controller/data/restic-password" + db_dump_schedule: "02:30" + restic_schedule: "03:00" + retention: + keep_daily: 7 + keep_weekly: 4 + keep_monthly: 6 + prune_schedule: "weekly" + +monitoring: + enabled: true + healthchecks_base: "https://status.felhom.eu" + ping_uuids: + heartbeat: "${WIZ_HC_HEARTBEAT}" + system_health: "${WIZ_HC_SYSTEM}" + db_dump: "${WIZ_HC_DBDUMP}" + backup: "${WIZ_HC_BACKUP}" + backup_integrity: "${WIZ_HC_INTEGRITY}" + system_health_interval: "5m" + health_check_schedule: "06:00" + thresholds: + disk_warn_percent: 80 + disk_crit_percent: 90 + backup_max_age_hours: 36 + cpu_warn_percent: 90 + memory_warn_percent: 85 + temperature_warn_celsius: 75 + +hub: + enabled: true + url: "https://hub.felhom.eu" + api_key: "094091de545ce28795c47ac2158fc30750db5c24a621c49329b001ee8db57fb8" + push_interval: "15m" + +self_update: + enabled: true + check_interval: "6h" + image: "gitea.dooplex.hu/admin/felhom-controller" + auto_update: false + health_timeout_seconds: 60 + +notifications: + customer_events: + - "disk_warning" + - "backup_failed" + - "update_available" + - "security_update" + operator_events: + - "disk_critical" + - "backup_failed" + - "self_update_failed" + - "container_unhealthy" + +logging: + level: "info" + file: "" + max_size_mb: 10 + max_files: 3 + +assets: + source_url: "https://felhom.eu" +YAMLEOF + + chmod 600 "${CONTROLLER_DIR}/controller.yaml" + log_success "controller.yaml generated at ${CONTROLLER_DIR}/controller.yaml" +} + +#------------------------------------------------------------------------------- +# Deploy felhom-controller +#------------------------------------------------------------------------------- +install_controller() { + local step_num=6 + [[ "$SELF_SIGNED_CERT" == true ]] && ((step_num++)) + [[ -n "${WIZ_CF_TUNNEL_TOKEN:-}" ]] && ((step_num++)) + [[ "$SKIP_FILEBROWSER" != true ]] && ((step_num++)) + log_step "${step_num}/$(get_total_steps) - Deploying felhom-controller..." + + if [[ "$DRY_RUN" == true ]]; then + echo -e "${CYAN}[DRY-RUN]${NC} Would deploy felhom-controller at felhom.${BASE_DOMAIN}" + return + fi + + mkdir -p "${CONTROLLER_DIR}" + + # Build certresolver label if Let's Encrypt is configured + local ctrl_certresolver_label="" + if [[ -n "$ACME_EMAIL" ]]; then + ctrl_certresolver_label=' - "traefik.http.routers.controller.tls.certresolver=letsencrypt"' + fi + + cat > "${CONTROLLER_DIR}/docker-compose.yml" << EOF +# Felhom Controller — Central management dashboard +# Domain: felhom.${BASE_DOMAIN} +# Deployed by docker-setup.sh v${SCRIPT_VERSION} + +services: + felhom-controller: + image: gitea.dooplex.hu/admin/felhom-controller:latest + container_name: felhom-controller + restart: unless-stopped + privileged: true + ports: + - "8080:8080" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ${CONTROLLER_DIR}/controller.yaml:/opt/docker/felhom-controller/controller.yaml:ro + - controller-data:/opt/docker/felhom-controller/data + - /opt/docker/stacks:/opt/docker/stacks + - /srv/backups:/srv/backups + - type: bind + source: /mnt + target: /mnt + bind: + propagation: rshared + - /sys:/host/sys:ro + - /etc/os-release:/host/etc/os-release:ro + - /etc/hostname:/host/etc/hostname:ro + - /dev:/host-dev:rw + - /etc/fstab:/host-fstab + - /run/udev:/run/udev:ro + environment: + - TZ=Europe/Budapest + labels: + - "traefik.enable=true" + - "traefik.http.routers.controller.rule=Host(\`felhom.${BASE_DOMAIN}\`)" + - "traefik.http.routers.controller.entrypoints=websecure" + - "traefik.http.routers.controller.tls=true" +${ctrl_certresolver_label} + - "traefik.http.services.controller.loadbalancer.server.port=8080" + - "traefik.docker.network=traefik-public" + - "felhom.managed=true" + - "felhom.component=controller" + networks: + - traefik-public + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"] + interval: 30s + timeout: 5s + start_period: 10s + retries: 3 + +volumes: + controller-data: + +networks: + traefik-public: + external: true +EOF + + cd "${CONTROLLER_DIR}" + log_info "Pulling felhom-controller image..." + docker compose pull -q + log_info "Starting felhom-controller..." + docker compose up -d --quiet-pull + + # Wait for health check + sleep 5 + if docker ps --filter "name=felhom-controller" --filter "status=running" -q | grep -q .; then + log_success "felhom-controller deployed at felhom.${BASE_DOMAIN}" + else + log_error "felhom-controller container is not running!" + docker compose logs --tail=20 felhom-controller + exit 1 + fi +} + #------------------------------------------------------------------------------- # Print summary #------------------------------------------------------------------------------- print_summary() { local server_ip server_ip=$(get_server_ip) - + echo "" echo -e "${BOLD}${GREEN}-==================================================================¬${NC}" echo -e "${BOLD}${GREEN}¦ Infrastructure Setup Complete! ¦${NC}" @@ -1452,20 +1784,24 @@ print_summary() { echo "" echo -e "${BOLD}Server IP:${NC} ${server_ip}" echo -e "${BOLD}Domain:${NC} *.${BASE_DOMAIN}" + echo -e "${BOLD}Customer:${NC} ${CUSTOMER_ID}" echo "" echo -e "${BOLD}Services:${NC}" + echo " • Felhom Controller: https://felhom.${BASE_DOMAIN}" + echo " L| Config: ${CONTROLLER_DIR}/controller.yaml" echo " • Traefik Dashboard: https://traefik.${BASE_DOMAIN}/dashboard/" echo " L| Credentials: admin / ${TRAEFIK_PASSWORD}" - if [[ -n "$CF_TUNNEL_TOKEN" ]]; then + if [[ -n "${WIZ_CF_TUNNEL_TOKEN:-}" ]]; then echo " • Cloudflare Tunnel: Active (routes configured in CF dashboard)" echo " L| Container: cloudflared" echo " L| Config: ${CLOUDFLARED_DIR}/docker-compose.yml" echo " L| Check: docker logs cloudflared" fi - if [[ "$SKIP_FILEBROWSER" != true ]] && [[ -n "$HDD_PATH" ]]; then + if [[ "$SKIP_FILEBROWSER" != true ]]; then echo " • FileBrowser: https://files.${BASE_DOMAIN}" echo " L| Default login: admin / admin (change immediately!)" fi + echo -e " • Hub reporting: ${GREEN}enabled${NC} (hub.felhom.eu)" echo "" echo -e "${BOLD}DNS Setup Required:${NC}" echo " Add wildcard record to your DNS (Pi-hole, router, etc.):" @@ -1495,22 +1831,14 @@ print_summary() { echo "" fi echo -e "${BOLD}${CYAN}===================================================================${NC}" - echo -e "${BOLD}${CYAN} DEPLOYING APPLICATION STACKS ${NC}" + echo -e "${BOLD}${CYAN} MANAGING APPLICATION STACKS ${NC}" echo -e "${BOLD}${CYAN}===================================================================${NC}" echo "" - echo " Deploy applications via the Felhom Controller dashboard:" + echo " Deploy and manage applications via the controller dashboard:" echo "" - echo " 1. Deploy felhom-controller (see controller/README.md)" - echo " 2. Open https://felhom.${BASE_DOMAIN}" - echo " 3. Browse available apps on the Alkalmazások page" - echo " 4. Click Telepítés to deploy" - if [[ -n "$CUSTOMER_ID" ]]; then - echo "" - echo -e "${BOLD}Customer:${NC} ${CUSTOMER_ID}" - echo "" - echo -e "${YELLOW}Note: No --customer specified. Templates use the generic catalog.${NC}" - echo -e "${YELLOW}Env vars (passwords, secrets) must be filled in manually.${NC}" - fi + echo " 1. Open https://felhom.${BASE_DOMAIN}" + echo " 2. Browse available apps on the Alkalmazások page" + echo " 3. Click Telepítés to deploy" echo "" if [[ -n "$CUSTOMER_ID" ]]; then echo -e "${BOLD}${YELLOW}Disaster Recovery:${NC}" @@ -1560,19 +1888,17 @@ main() { echo " 2. Configure network (Static IP: ${STATIC_IP:-DHCP})" echo " 3. Install Docker Engine + Compose" echo " 4. Install Traefik reverse proxy" - if [[ -n "$CF_TUNNEL_TOKEN" ]]; then - echo -e " 5. Install Cloudflare Tunnel connector: ${GREEN}yes${NC}" - else - echo -e " - Cloudflare Tunnel: ${CYAN}skip (no --cf-tunnel-token)${NC}" - fi if [[ "$SELF_SIGNED_CERT" == true ]]; then - echo " - Generate self-signed certificate: yes" + echo " 5. Generate self-signed certificate" fi - echo " - Install FileBrowser: $([[ "$SKIP_FILEBROWSER" == true ]] && echo "skip" || echo "$([[ -n "$HDD_PATH" ]] && echo "yes (HDD: $HDD_PATH)" || echo "skip (no --hdd-path)")")" + echo " - Run configuration wizard (generates controller.yaml)" + echo " - Install Cloudflare Tunnel (if configured in wizard)" + echo " - Install FileBrowser: $([[ "$SKIP_FILEBROWSER" == true ]] && echo "skip" || echo "yes (auto-discover drives)")" + echo " - Deploy felhom-controller" echo " - Install helper tools (ctop, lazydocker, aliases)" echo "" echo " Domain: *.${BASE_DOMAIN}" - echo " Customer: ${CUSTOMER_ID:-}" + echo " Customer: ${CUSTOMER_ID:-}" echo " Traefik password: ${TRAEFIK_PASSWORD}" if [[ -n "$ACME_EMAIL" && -n "$CF_DNS_API_TOKEN" ]]; then echo -e " TLS: ${GREEN}Let's Encrypt (Cloudflare DNS-01)${NC}" @@ -1586,29 +1912,28 @@ main() { else echo -e " TLS: ${RED}None (Traefik default cert)${NC}" fi - if [[ -n "$CF_TUNNEL_TOKEN" ]]; then - echo " CF tunnel token: ${CF_TUNNEL_TOKEN:0:12}... (masked)" - fi echo "" - + if [[ "$DRY_RUN" == true ]]; then echo -e "${YELLOW}DRY RUN - no changes will be made${NC}" echo "" fi - + read -p "Continue? [y/N] " -n 1 -r echo [[ ! $REPLY =~ ^[Yy]$ ]] && exit 0 - + install_base_packages configure_static_ip install_docker install_traefik - install_cloudflare_tunnel generate_self_signed_cert + run_config_wizard + install_cloudflare_tunnel install_filebrowser + install_controller install_tools_and_configure - + print_summary }